{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<table style=\"float:left; border:none\">\n",
    "   <tr style=\"border:none\">\n",
    "       <td style=\"border:none\">\n",
    "           <a href=\"https://bokeh.org/\">     \n",
    "           <img \n",
    "               src=\"assets/bokeh-transparent.png\" \n",
    "               style=\"width:50px\"\n",
    "           >\n",
    "           </a>    \n",
    "       </td>\n",
    "       <td style=\"border:none\">\n",
    "           <h1>Bokeh Tutorial</h1>\n",
    "       </td>\n",
    "   </tr>\n",
    "</table>\n",
    "\n",
    "<div style=\"float:right;\"><h2>A1. Models and Primitives</h2></div>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Overview\n",
    "\n",
    "Although typically referred to by a single name, Bokeh comprises two separate libraries. \n",
    "\n",
    "The first component is a JavaScript library, BokehJS, that runs in the browser. This library is responsible for all of the rendering and user interaction. Its input is a collection of declarative JSON objects that comprise a “scenegraph”. The objects in this scenegraph describe everything that BokehJS should handle: what plots and widgets are present and in what arrangement, what tools and renderers and axes the plots will have, etc. These JSON objects are converted into JavaScript objects in the browser.\n",
    "\n",
    "The second component is a library in Python (or other languages) that can generate the JSON described above. In the Python Bokeh library, this is accomplished at the lowest level by exposing a set of `Model` subclasses that exactly mirror the set of BokehJS Models that are use in the browser. Most of the models are very simple, usually consisting of a few property attributes and no methods. Model attributes can either be configured when the model is created, or later by setting attribute values on the model object:\n",
    "\n",
    "#### properties can be configured when a model object is initialized\n",
    "```python\n",
    "glyph = Rect(x=\"x\", y=\"y2\", w=10, h=20, line_color=None)\n",
    "```\n",
    "\n",
    "#### properties can be configured by assigning values to attributes on exitsting models\n",
    "```python\n",
    "glyph.fill_alpha = 0.5\n",
    "glyph.fill_color = \"navy\"\n",
    "```\n",
    "\n",
    "These methods of configuration work in general for all Bokeh models. \n",
    "\n",
    "Bokeh models are eventually collected into a Bokeh `Document` for serialization between BokehJS and the Bokeh Python bindings. As an example, the following image shows an example document for simple plot:\n",
    "\n",
    "![Document Structure](assets/document.svg)\n",
    "\n",
    "Such a document could be created at a high level using the `bokeh.plotting` API, which automatically assembles any models such as axes and grids and toolbars in a reasonable way. However it is also possible to assemble all the components \"by hand\" using the low-level `bokeh.models` API. Using the `bokeh.models` interface provides complete control over how Bokeh plots and Bokeh widgets are put together and configured. However, it provides no help with assembling the models in meaningful or correct ways. It is entirely up to developers to build the scenegraph “by hand”. \n",
    "\n",
    "For more information about the details of all Bokeh models, consult the [Reference Guide](https://bokeh.pydata.org/en/latest/docs/reference.html)."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Example Walkthrough\n",
    "\n",
    "Let's try to reproduce this NYTimes interactive chart [Usain Bolt vs. 116 years of Olympic sprinters](http://www.nytimes.com/interactive/2012/08/05/sports/olympics/the-100-meter-dash-one-race-every-medalist-ever.html) using the `bokeh.models` interface."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The first thing we need is to get the data. The data for this chart is located in the ``bokeh.sampledata`` module as a Pandas DataFrame. You can see the first ten rows below:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>Name</th>\n",
       "      <th>Country</th>\n",
       "      <th>Medal</th>\n",
       "      <th>Time</th>\n",
       "      <th>Year</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>Usain Bolt</td>\n",
       "      <td>JAM</td>\n",
       "      <td>GOLD</td>\n",
       "      <td>9.63</td>\n",
       "      <td>2012</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>Yohan Blake</td>\n",
       "      <td>JAM</td>\n",
       "      <td>SILVER</td>\n",
       "      <td>9.75</td>\n",
       "      <td>2012</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>Justin Gatlin</td>\n",
       "      <td>USA</td>\n",
       "      <td>BRONZE</td>\n",
       "      <td>9.79</td>\n",
       "      <td>2012</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>Usain Bolt</td>\n",
       "      <td>JAM</td>\n",
       "      <td>GOLD</td>\n",
       "      <td>9.69</td>\n",
       "      <td>2008</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>Richard Thompson</td>\n",
       "      <td>TRI</td>\n",
       "      <td>SILVER</td>\n",
       "      <td>9.89</td>\n",
       "      <td>2008</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>5</th>\n",
       "      <td>Walter Dix</td>\n",
       "      <td>USA</td>\n",
       "      <td>BRONZE</td>\n",
       "      <td>9.91</td>\n",
       "      <td>2008</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>6</th>\n",
       "      <td>Justin Gatlin</td>\n",
       "      <td>USA</td>\n",
       "      <td>GOLD</td>\n",
       "      <td>9.85</td>\n",
       "      <td>2004</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>7</th>\n",
       "      <td>Francis Obikwelu</td>\n",
       "      <td>POR</td>\n",
       "      <td>SILVER</td>\n",
       "      <td>9.86</td>\n",
       "      <td>2004</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>8</th>\n",
       "      <td>Maurice Greene</td>\n",
       "      <td>USA</td>\n",
       "      <td>BRONZE</td>\n",
       "      <td>9.87</td>\n",
       "      <td>2004</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>9</th>\n",
       "      <td>Maurice Greene</td>\n",
       "      <td>USA</td>\n",
       "      <td>GOLD</td>\n",
       "      <td>9.87</td>\n",
       "      <td>2000</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "               Name Country   Medal  Time  Year\n",
       "0        Usain Bolt     JAM    GOLD  9.63  2012\n",
       "1       Yohan Blake     JAM  SILVER  9.75  2012\n",
       "2     Justin Gatlin     USA  BRONZE  9.79  2012\n",
       "3        Usain Bolt     JAM    GOLD  9.69  2008\n",
       "4  Richard Thompson     TRI  SILVER  9.89  2008\n",
       "5        Walter Dix     USA  BRONZE  9.91  2008\n",
       "6     Justin Gatlin     USA    GOLD  9.85  2004\n",
       "7  Francis Obikwelu     POR  SILVER  9.86  2004\n",
       "8    Maurice Greene     USA  BRONZE  9.87  2004\n",
       "9    Maurice Greene     USA    GOLD  9.87  2000"
      ]
     },
     "execution_count": 1,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from bokeh.sampledata.sprint import sprint\n",
    "sprint[:10]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Next we import some of the Bokeh models that need to be assembled to make a plot. At a minimum, we need to start with ``Plot``, the glyphs (``Circle`` and ``Text``) we want to display, as well as ``ColumnDataSource`` to hold the data and range obejcts to set the plot bounds. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "from bokeh.io import output_notebook, show\n",
    "from bokeh.models.glyphs import Circle, Text\n",
    "from bokeh.models import ColumnDataSource, Range1d, DataRange1d, Plot"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "\n",
       "    <div class=\"bk-root\">\n",
       "        <a href=\"https://bokeh.org\" target=\"_blank\" class=\"bk-logo bk-logo-small bk-logo-notebook\"></a>\n",
       "        <span id=\"1001\">Loading BokehJS ...</span>\n",
       "    </div>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "application/javascript": [
       "\n",
       "(function(root) {\n",
       "  function now() {\n",
       "    return new Date();\n",
       "  }\n",
       "\n",
       "  var force = true;\n",
       "\n",
       "  if (typeof root._bokeh_onload_callbacks === \"undefined\" || force === true) {\n",
       "    root._bokeh_onload_callbacks = [];\n",
       "    root._bokeh_is_loading = undefined;\n",
       "  }\n",
       "\n",
       "  var JS_MIME_TYPE = 'application/javascript';\n",
       "  var HTML_MIME_TYPE = 'text/html';\n",
       "  var EXEC_MIME_TYPE = 'application/vnd.bokehjs_exec.v0+json';\n",
       "  var CLASS_NAME = 'output_bokeh rendered_html';\n",
       "\n",
       "  /**\n",
       "   * Render data to the DOM node\n",
       "   */\n",
       "  function render(props, node) {\n",
       "    var script = document.createElement(\"script\");\n",
       "    node.appendChild(script);\n",
       "  }\n",
       "\n",
       "  /**\n",
       "   * Handle when an output is cleared or removed\n",
       "   */\n",
       "  function handleClearOutput(event, handle) {\n",
       "    var cell = handle.cell;\n",
       "\n",
       "    var id = cell.output_area._bokeh_element_id;\n",
       "    var server_id = cell.output_area._bokeh_server_id;\n",
       "    // Clean up Bokeh references\n",
       "    if (id != null && id in Bokeh.index) {\n",
       "      Bokeh.index[id].model.document.clear();\n",
       "      delete Bokeh.index[id];\n",
       "    }\n",
       "\n",
       "    if (server_id !== undefined) {\n",
       "      // Clean up Bokeh references\n",
       "      var cmd = \"from bokeh.io.state import curstate; print(curstate().uuid_to_server['\" + server_id + \"'].get_sessions()[0].document.roots[0]._id)\";\n",
       "      cell.notebook.kernel.execute(cmd, {\n",
       "        iopub: {\n",
       "          output: function(msg) {\n",
       "            var id = msg.content.text.trim();\n",
       "            if (id in Bokeh.index) {\n",
       "              Bokeh.index[id].model.document.clear();\n",
       "              delete Bokeh.index[id];\n",
       "            }\n",
       "          }\n",
       "        }\n",
       "      });\n",
       "      // Destroy server and session\n",
       "      var cmd = \"import bokeh.io.notebook as ion; ion.destroy_server('\" + server_id + \"')\";\n",
       "      cell.notebook.kernel.execute(cmd);\n",
       "    }\n",
       "  }\n",
       "\n",
       "  /**\n",
       "   * Handle when a new output is added\n",
       "   */\n",
       "  function handleAddOutput(event, handle) {\n",
       "    var output_area = handle.output_area;\n",
       "    var output = handle.output;\n",
       "\n",
       "    // limit handleAddOutput to display_data with EXEC_MIME_TYPE content only\n",
       "    if ((output.output_type != \"display_data\") || (!output.data.hasOwnProperty(EXEC_MIME_TYPE))) {\n",
       "      return\n",
       "    }\n",
       "\n",
       "    var toinsert = output_area.element.find(\".\" + CLASS_NAME.split(' ')[0]);\n",
       "\n",
       "    if (output.metadata[EXEC_MIME_TYPE][\"id\"] !== undefined) {\n",
       "      toinsert[toinsert.length - 1].firstChild.textContent = output.data[JS_MIME_TYPE];\n",
       "      // store reference to embed id on output_area\n",
       "      output_area._bokeh_element_id = output.metadata[EXEC_MIME_TYPE][\"id\"];\n",
       "    }\n",
       "    if (output.metadata[EXEC_MIME_TYPE][\"server_id\"] !== undefined) {\n",
       "      var bk_div = document.createElement(\"div\");\n",
       "      bk_div.innerHTML = output.data[HTML_MIME_TYPE];\n",
       "      var script_attrs = bk_div.children[0].attributes;\n",
       "      for (var i = 0; i < script_attrs.length; i++) {\n",
       "        toinsert[toinsert.length - 1].firstChild.setAttribute(script_attrs[i].name, script_attrs[i].value);\n",
       "        toinsert[toinsert.length - 1].firstChild.textContent = bk_div.children[0].textContent\n",
       "      }\n",
       "      // store reference to server id on output_area\n",
       "      output_area._bokeh_server_id = output.metadata[EXEC_MIME_TYPE][\"server_id\"];\n",
       "    }\n",
       "  }\n",
       "\n",
       "  function register_renderer(events, OutputArea) {\n",
       "\n",
       "    function append_mime(data, metadata, element) {\n",
       "      // create a DOM node to render to\n",
       "      var toinsert = this.create_output_subarea(\n",
       "        metadata,\n",
       "        CLASS_NAME,\n",
       "        EXEC_MIME_TYPE\n",
       "      );\n",
       "      this.keyboard_manager.register_events(toinsert);\n",
       "      // Render to node\n",
       "      var props = {data: data, metadata: metadata[EXEC_MIME_TYPE]};\n",
       "      render(props, toinsert[toinsert.length - 1]);\n",
       "      element.append(toinsert);\n",
       "      return toinsert\n",
       "    }\n",
       "\n",
       "    /* Handle when an output is cleared or removed */\n",
       "    events.on('clear_output.CodeCell', handleClearOutput);\n",
       "    events.on('delete.Cell', handleClearOutput);\n",
       "\n",
       "    /* Handle when a new output is added */\n",
       "    events.on('output_added.OutputArea', handleAddOutput);\n",
       "\n",
       "    /**\n",
       "     * Register the mime type and append_mime function with output_area\n",
       "     */\n",
       "    OutputArea.prototype.register_mime_type(EXEC_MIME_TYPE, append_mime, {\n",
       "      /* Is output safe? */\n",
       "      safe: true,\n",
       "      /* Index of renderer in `output_area.display_order` */\n",
       "      index: 0\n",
       "    });\n",
       "  }\n",
       "\n",
       "  // register the mime type if in Jupyter Notebook environment and previously unregistered\n",
       "  if (root.Jupyter !== undefined) {\n",
       "    var events = require('base/js/events');\n",
       "    var OutputArea = require('notebook/js/outputarea').OutputArea;\n",
       "\n",
       "    if (OutputArea.prototype.mime_types().indexOf(EXEC_MIME_TYPE) == -1) {\n",
       "      register_renderer(events, OutputArea);\n",
       "    }\n",
       "  }\n",
       "\n",
       "  \n",
       "  if (typeof (root._bokeh_timeout) === \"undefined\" || force === true) {\n",
       "    root._bokeh_timeout = Date.now() + 5000;\n",
       "    root._bokeh_failed_load = false;\n",
       "  }\n",
       "\n",
       "  var NB_LOAD_WARNING = {'data': {'text/html':\n",
       "     \"<div style='background-color: #fdd'>\\n\"+\n",
       "     \"<p>\\n\"+\n",
       "     \"BokehJS does not appear to have successfully loaded. If loading BokehJS from CDN, this \\n\"+\n",
       "     \"may be due to a slow or bad network connection. Possible fixes:\\n\"+\n",
       "     \"</p>\\n\"+\n",
       "     \"<ul>\\n\"+\n",
       "     \"<li>re-rerun `output_notebook()` to attempt to load from CDN again, or</li>\\n\"+\n",
       "     \"<li>use INLINE resources instead, as so:</li>\\n\"+\n",
       "     \"</ul>\\n\"+\n",
       "     \"<code>\\n\"+\n",
       "     \"from bokeh.resources import INLINE\\n\"+\n",
       "     \"output_notebook(resources=INLINE)\\n\"+\n",
       "     \"</code>\\n\"+\n",
       "     \"</div>\"}};\n",
       "\n",
       "  function display_loaded() {\n",
       "    var el = document.getElementById(\"1001\");\n",
       "    if (el != null) {\n",
       "      el.textContent = \"BokehJS is loading...\";\n",
       "    }\n",
       "    if (root.Bokeh !== undefined) {\n",
       "      if (el != null) {\n",
       "        el.textContent = \"BokehJS \" + root.Bokeh.version + \" successfully loaded.\";\n",
       "      }\n",
       "    } else if (Date.now() < root._bokeh_timeout) {\n",
       "      setTimeout(display_loaded, 100)\n",
       "    }\n",
       "  }\n",
       "\n",
       "\n",
       "  function run_callbacks() {\n",
       "    try {\n",
       "      root._bokeh_onload_callbacks.forEach(function(callback) {\n",
       "        if (callback != null)\n",
       "          callback();\n",
       "      });\n",
       "    } finally {\n",
       "      delete root._bokeh_onload_callbacks\n",
       "    }\n",
       "    console.debug(\"Bokeh: all callbacks have finished\");\n",
       "  }\n",
       "\n",
       "  function load_libs(css_urls, js_urls, callback) {\n",
       "    if (css_urls == null) css_urls = [];\n",
       "    if (js_urls == null) js_urls = [];\n",
       "\n",
       "    root._bokeh_onload_callbacks.push(callback);\n",
       "    if (root._bokeh_is_loading > 0) {\n",
       "      console.debug(\"Bokeh: BokehJS is being loaded, scheduling callback at\", now());\n",
       "      return null;\n",
       "    }\n",
       "    if (js_urls == null || js_urls.length === 0) {\n",
       "      run_callbacks();\n",
       "      return null;\n",
       "    }\n",
       "    console.debug(\"Bokeh: BokehJS not loaded, scheduling load and callback at\", now());\n",
       "    root._bokeh_is_loading = css_urls.length + js_urls.length;\n",
       "\n",
       "    function on_load() {\n",
       "      root._bokeh_is_loading--;\n",
       "      if (root._bokeh_is_loading === 0) {\n",
       "        console.debug(\"Bokeh: all BokehJS libraries/stylesheets loaded\");\n",
       "        run_callbacks()\n",
       "      }\n",
       "    }\n",
       "\n",
       "    function on_error() {\n",
       "      console.error(\"failed to load \" + url);\n",
       "    }\n",
       "\n",
       "    for (var i = 0; i < css_urls.length; i++) {\n",
       "      var url = css_urls[i];\n",
       "      const element = document.createElement(\"link\");\n",
       "      element.onload = on_load;\n",
       "      element.onerror = on_error;\n",
       "      element.rel = \"stylesheet\";\n",
       "      element.type = \"text/css\";\n",
       "      element.href = url;\n",
       "      console.debug(\"Bokeh: injecting link tag for BokehJS stylesheet: \", url);\n",
       "      document.body.appendChild(element);\n",
       "    }\n",
       "\n",
       "    const hashes = {\"https://cdn.bokeh.org/bokeh/release/bokeh-2.0.1.min.js\": \"JpP8FXbgAZLkfur7LiK3j9AGBhHNIvF742meBJrjO2ShJDhCG2I1uVvW+0DUtrmc\", \"https://cdn.bokeh.org/bokeh/release/bokeh-widgets-2.0.1.min.js\": \"xZlADit0Q04ISQEdKg2k3L4W9AwQBAuDs9nJL9fM/WwzL1tEU9VPNezOFX0nLEAz\", \"https://cdn.bokeh.org/bokeh/release/bokeh-tables-2.0.1.min.js\": \"4BuPRZkdMKSnj3zoxiNrQ86XgNw0rYmBOxe7nshquXwwcauupgBF2DHLVG1WuZlV\", \"https://cdn.bokeh.org/bokeh/release/bokeh-gl-2.0.1.min.js\": \"Dv1SQ87hmDqK6S5OhBf0bCuwAEvL5QYL0PuR/F1SPVhCS/r/abjkbpKDYL2zeM19\"};\n",
       "\n",
       "    for (var i = 0; i < js_urls.length; i++) {\n",
       "      var url = js_urls[i];\n",
       "      var element = document.createElement('script');\n",
       "      element.onload = on_load;\n",
       "      element.onerror = on_error;\n",
       "      element.async = false;\n",
       "      element.src = url;\n",
       "      if (url in hashes) {\n",
       "        element.crossOrigin = \"anonymous\";\n",
       "        element.integrity = \"sha384-\" + hashes[url];\n",
       "      }\n",
       "      console.debug(\"Bokeh: injecting script tag for BokehJS library: \", url);\n",
       "      document.head.appendChild(element);\n",
       "    }\n",
       "  };var element = document.getElementById(\"1001\");\n",
       "  if (element == null) {\n",
       "    console.error(\"Bokeh: ERROR: autoload.js configured with elementid '1001' but no matching script tag was found. \")\n",
       "    return false;\n",
       "  }\n",
       "\n",
       "  function inject_raw_css(css) {\n",
       "    const element = document.createElement(\"style\");\n",
       "    element.appendChild(document.createTextNode(css));\n",
       "    document.body.appendChild(element);\n",
       "  }\n",
       "\n",
       "  \n",
       "  var js_urls = [\"https://cdn.bokeh.org/bokeh/release/bokeh-2.0.1.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-widgets-2.0.1.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-tables-2.0.1.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-gl-2.0.1.min.js\"];\n",
       "  var css_urls = [];\n",
       "  \n",
       "\n",
       "  var inline_js = [\n",
       "    function(Bokeh) {\n",
       "      Bokeh.set_log_level(\"info\");\n",
       "    },\n",
       "    function(Bokeh) {\n",
       "    \n",
       "    \n",
       "    }\n",
       "  ];\n",
       "\n",
       "  function run_inline_js() {\n",
       "    \n",
       "    if (root.Bokeh !== undefined || force === true) {\n",
       "      \n",
       "    for (var i = 0; i < inline_js.length; i++) {\n",
       "      inline_js[i].call(root, root.Bokeh);\n",
       "    }\n",
       "    if (force === true) {\n",
       "        display_loaded();\n",
       "      }} else if (Date.now() < root._bokeh_timeout) {\n",
       "      setTimeout(run_inline_js, 100);\n",
       "    } else if (!root._bokeh_failed_load) {\n",
       "      console.log(\"Bokeh: BokehJS failed to load within specified timeout.\");\n",
       "      root._bokeh_failed_load = true;\n",
       "    } else if (force !== true) {\n",
       "      var cell = $(document.getElementById(\"1001\")).parents('.cell').data().cell;\n",
       "      cell.output_area.append_execute_result(NB_LOAD_WARNING)\n",
       "    }\n",
       "\n",
       "  }\n",
       "\n",
       "  if (root._bokeh_is_loading === 0) {\n",
       "    console.debug(\"Bokeh: BokehJS loaded, going straight to plotting\");\n",
       "    run_inline_js();\n",
       "  } else {\n",
       "    load_libs(css_urls, js_urls, function() {\n",
       "      console.debug(\"Bokeh: BokehJS plotting callback run at\", now());\n",
       "      run_inline_js();\n",
       "    });\n",
       "  }\n",
       "}(window));"
      ],
      "application/vnd.bokehjs_load.v0+json": "\n(function(root) {\n  function now() {\n    return new Date();\n  }\n\n  var force = true;\n\n  if (typeof root._bokeh_onload_callbacks === \"undefined\" || force === true) {\n    root._bokeh_onload_callbacks = [];\n    root._bokeh_is_loading = undefined;\n  }\n\n  \n\n  \n  if (typeof (root._bokeh_timeout) === \"undefined\" || force === true) {\n    root._bokeh_timeout = Date.now() + 5000;\n    root._bokeh_failed_load = false;\n  }\n\n  var NB_LOAD_WARNING = {'data': {'text/html':\n     \"<div style='background-color: #fdd'>\\n\"+\n     \"<p>\\n\"+\n     \"BokehJS does not appear to have successfully loaded. If loading BokehJS from CDN, this \\n\"+\n     \"may be due to a slow or bad network connection. Possible fixes:\\n\"+\n     \"</p>\\n\"+\n     \"<ul>\\n\"+\n     \"<li>re-rerun `output_notebook()` to attempt to load from CDN again, or</li>\\n\"+\n     \"<li>use INLINE resources instead, as so:</li>\\n\"+\n     \"</ul>\\n\"+\n     \"<code>\\n\"+\n     \"from bokeh.resources import INLINE\\n\"+\n     \"output_notebook(resources=INLINE)\\n\"+\n     \"</code>\\n\"+\n     \"</div>\"}};\n\n  function display_loaded() {\n    var el = document.getElementById(\"1001\");\n    if (el != null) {\n      el.textContent = \"BokehJS is loading...\";\n    }\n    if (root.Bokeh !== undefined) {\n      if (el != null) {\n        el.textContent = \"BokehJS \" + root.Bokeh.version + \" successfully loaded.\";\n      }\n    } else if (Date.now() < root._bokeh_timeout) {\n      setTimeout(display_loaded, 100)\n    }\n  }\n\n\n  function run_callbacks() {\n    try {\n      root._bokeh_onload_callbacks.forEach(function(callback) {\n        if (callback != null)\n          callback();\n      });\n    } finally {\n      delete root._bokeh_onload_callbacks\n    }\n    console.debug(\"Bokeh: all callbacks have finished\");\n  }\n\n  function load_libs(css_urls, js_urls, callback) {\n    if (css_urls == null) css_urls = [];\n    if (js_urls == null) js_urls = [];\n\n    root._bokeh_onload_callbacks.push(callback);\n    if (root._bokeh_is_loading > 0) {\n      console.debug(\"Bokeh: BokehJS is being loaded, scheduling callback at\", now());\n      return null;\n    }\n    if (js_urls == null || js_urls.length === 0) {\n      run_callbacks();\n      return null;\n    }\n    console.debug(\"Bokeh: BokehJS not loaded, scheduling load and callback at\", now());\n    root._bokeh_is_loading = css_urls.length + js_urls.length;\n\n    function on_load() {\n      root._bokeh_is_loading--;\n      if (root._bokeh_is_loading === 0) {\n        console.debug(\"Bokeh: all BokehJS libraries/stylesheets loaded\");\n        run_callbacks()\n      }\n    }\n\n    function on_error() {\n      console.error(\"failed to load \" + url);\n    }\n\n    for (var i = 0; i < css_urls.length; i++) {\n      var url = css_urls[i];\n      const element = document.createElement(\"link\");\n      element.onload = on_load;\n      element.onerror = on_error;\n      element.rel = \"stylesheet\";\n      element.type = \"text/css\";\n      element.href = url;\n      console.debug(\"Bokeh: injecting link tag for BokehJS stylesheet: \", url);\n      document.body.appendChild(element);\n    }\n\n    const hashes = {\"https://cdn.bokeh.org/bokeh/release/bokeh-2.0.1.min.js\": \"JpP8FXbgAZLkfur7LiK3j9AGBhHNIvF742meBJrjO2ShJDhCG2I1uVvW+0DUtrmc\", \"https://cdn.bokeh.org/bokeh/release/bokeh-widgets-2.0.1.min.js\": \"xZlADit0Q04ISQEdKg2k3L4W9AwQBAuDs9nJL9fM/WwzL1tEU9VPNezOFX0nLEAz\", \"https://cdn.bokeh.org/bokeh/release/bokeh-tables-2.0.1.min.js\": \"4BuPRZkdMKSnj3zoxiNrQ86XgNw0rYmBOxe7nshquXwwcauupgBF2DHLVG1WuZlV\", \"https://cdn.bokeh.org/bokeh/release/bokeh-gl-2.0.1.min.js\": \"Dv1SQ87hmDqK6S5OhBf0bCuwAEvL5QYL0PuR/F1SPVhCS/r/abjkbpKDYL2zeM19\"};\n\n    for (var i = 0; i < js_urls.length; i++) {\n      var url = js_urls[i];\n      var element = document.createElement('script');\n      element.onload = on_load;\n      element.onerror = on_error;\n      element.async = false;\n      element.src = url;\n      if (url in hashes) {\n        element.crossOrigin = \"anonymous\";\n        element.integrity = \"sha384-\" + hashes[url];\n      }\n      console.debug(\"Bokeh: injecting script tag for BokehJS library: \", url);\n      document.head.appendChild(element);\n    }\n  };var element = document.getElementById(\"1001\");\n  if (element == null) {\n    console.error(\"Bokeh: ERROR: autoload.js configured with elementid '1001' but no matching script tag was found. \")\n    return false;\n  }\n\n  function inject_raw_css(css) {\n    const element = document.createElement(\"style\");\n    element.appendChild(document.createTextNode(css));\n    document.body.appendChild(element);\n  }\n\n  \n  var js_urls = [\"https://cdn.bokeh.org/bokeh/release/bokeh-2.0.1.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-widgets-2.0.1.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-tables-2.0.1.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-gl-2.0.1.min.js\"];\n  var css_urls = [];\n  \n\n  var inline_js = [\n    function(Bokeh) {\n      Bokeh.set_log_level(\"info\");\n    },\n    function(Bokeh) {\n    \n    \n    }\n  ];\n\n  function run_inline_js() {\n    \n    if (root.Bokeh !== undefined || force === true) {\n      \n    for (var i = 0; i < inline_js.length; i++) {\n      inline_js[i].call(root, root.Bokeh);\n    }\n    if (force === true) {\n        display_loaded();\n      }} else if (Date.now() < root._bokeh_timeout) {\n      setTimeout(run_inline_js, 100);\n    } else if (!root._bokeh_failed_load) {\n      console.log(\"Bokeh: BokehJS failed to load within specified timeout.\");\n      root._bokeh_failed_load = true;\n    } else if (force !== true) {\n      var cell = $(document.getElementById(\"1001\")).parents('.cell').data().cell;\n      cell.output_area.append_execute_result(NB_LOAD_WARNING)\n    }\n\n  }\n\n  if (root._bokeh_is_loading === 0) {\n    console.debug(\"Bokeh: BokehJS loaded, going straight to plotting\");\n    run_inline_js();\n  } else {\n    load_libs(css_urls, js_urls, function() {\n      console.debug(\"Bokeh: BokehJS plotting callback run at\", now());\n      run_inline_js();\n    });\n  }\n}(window));"
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "output_notebook()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Setting up Data\n",
    "\n",
    "Next we need set up all the columns we want in our column data source. Here we add a few extra columns like `MetersBack` and `SelectedName` that we will use for a `HoverTool` later."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "abbrev_to_country = {\n",
    "    \"USA\": \"United States\",\n",
    "    \"GBR\": \"Britain\",\n",
    "    \"JAM\": \"Jamaica\",\n",
    "    \"CAN\": \"Canada\",\n",
    "    \"TRI\": \"Trinidad and Tobago\",\n",
    "    \"AUS\": \"Australia\",\n",
    "    \"GER\": \"Germany\",\n",
    "    \"CUB\": \"Cuba\",\n",
    "    \"NAM\": \"Namibia\",\n",
    "    \"URS\": \"Soviet Union\",\n",
    "    \"BAR\": \"Barbados\",\n",
    "    \"BUL\": \"Bulgaria\",\n",
    "    \"HUN\": \"Hungary\",\n",
    "    \"NED\": \"Netherlands\",\n",
    "    \"NZL\": \"New Zealand\",\n",
    "    \"PAN\": \"Panama\",\n",
    "    \"POR\": \"Portugal\",\n",
    "    \"RSA\": \"South Africa\",\n",
    "    \"EUA\": \"United Team of Germany\",\n",
    "}\n",
    "\n",
    "fill_color = { \"gold\": \"#efcf6d\", \"silver\": \"#cccccc\", \"bronze\": \"#c59e8a\" }\n",
    "line_color = { \"gold\": \"#c8a850\", \"silver\": \"#b0b0b1\", \"bronze\": \"#98715d\" }\n",
    "\n",
    "def selected_name(name, medal, year):\n",
    "    return name if medal == \"gold\" and year in [1988, 1968, 1936, 1896] else \"\"\n",
    "\n",
    "t0 = sprint.Time[0]\n",
    "\n",
    "sprint[\"Abbrev\"]       = sprint.Country\n",
    "sprint[\"Country\"]      = sprint.Abbrev.map(lambda abbr: abbrev_to_country[abbr])\n",
    "sprint[\"Medal\"]        = sprint.Medal.map(lambda medal: medal.lower())\n",
    "sprint[\"Speed\"]        = 100.0/sprint.Time\n",
    "sprint[\"MetersBack\"]   = 100.0*(1.0 - t0/sprint.Time)\n",
    "sprint[\"MedalFill\"]    = sprint.Medal.map(lambda medal: fill_color[medal])\n",
    "sprint[\"MedalLine\"]    = sprint.Medal.map(lambda medal: line_color[medal])\n",
    "sprint[\"SelectedName\"] = sprint[[\"Name\", \"Medal\", \"Year\"]].apply(tuple, axis=1).map(lambda args: selected_name(*args))\n",
    "\n",
    "source = ColumnDataSource(sprint)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Building in stages\n",
    "\n",
    "Let's build up our plot in stages, stopping to check the output along the way to see how things look.\n",
    "\n",
    "As we go through, note the three methods that `Plot`, `Chart`, and `Figure` all have:\n",
    "\n",
    "* `p.add_glyph`\n",
    "* `p.add_tools`\n",
    "* `p.add_layout`\n",
    "\n",
    "These are actually small convenience methods that help us add models to `Plot` objects in the correct way."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Basic Plot with Just Glyphs\n",
    "\n",
    "First we create just the `Plot` with a title and some basic styling applied, as well add a few `Circle` glyphs for the actual race data. To manually configure glyphs, we first create a glyph object (e.g., `Text` or `Circle`) that is configured with the visual properties we want as well as the data columns to use for coordinates, etc. Then we call `plot.add_glyph` with the glyph, and the data source that the glyph should use. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [],
   "source": [
    "plot_options = dict(plot_width=800, plot_height=480, toolbar_location=None, outline_line_color=None)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [],
   "source": [
    "medal_glyph = Circle(x=\"MetersBack\", y=\"Year\", size=10, fill_color=\"MedalFill\", \n",
    "                     line_color=\"MedalLine\", fill_alpha=0.5)\n",
    "\n",
    "athlete_glyph = Text(x=\"MetersBack\", y=\"Year\", x_offset=10, text=\"SelectedName\",\n",
    "                     text_align=\"left\", text_baseline=\"middle\", text_font_size=\"9pt\")\n",
    "\n",
    "no_olympics_glyph = Text(x=7.5, y=1942, text=[\"No Olympics in 1940 or 1944\"],\n",
    "                         text_align=\"center\", text_baseline=\"middle\",\n",
    "                         text_font_size=\"9pt\", text_font_style=\"italic\", text_color=\"silver\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div style=\"display: table;\"><div style=\"display: table-row;\"><div style=\"display: table-cell;\"><b title=\"bokeh.models.renderers.GlyphRenderer\">GlyphRenderer</b>(</div><div style=\"display: table-cell;\">id&nbsp;=&nbsp;'1017', <span id=\"1020\" style=\"cursor: pointer;\">&hellip;)</span></div></div><div class=\"1019\" style=\"display: none;\"><div style=\"display: table-cell;\"></div><div style=\"display: table-cell;\">data_source&nbsp;=&nbsp;ColumnDataSource(id='1016', ...),</div></div><div class=\"1019\" style=\"display: none;\"><div style=\"display: table-cell;\"></div><div style=\"display: table-cell;\">glyph&nbsp;=&nbsp;Text(id='1005', ...),</div></div><div class=\"1019\" style=\"display: none;\"><div style=\"display: table-cell;\"></div><div style=\"display: table-cell;\">hover_glyph&nbsp;=&nbsp;None,</div></div><div class=\"1019\" style=\"display: none;\"><div style=\"display: table-cell;\"></div><div style=\"display: table-cell;\">js_event_callbacks&nbsp;=&nbsp;{},</div></div><div class=\"1019\" style=\"display: none;\"><div style=\"display: table-cell;\"></div><div style=\"display: table-cell;\">js_property_callbacks&nbsp;=&nbsp;{},</div></div><div class=\"1019\" style=\"display: none;\"><div style=\"display: table-cell;\"></div><div style=\"display: table-cell;\">level&nbsp;=&nbsp;'glyph',</div></div><div class=\"1019\" style=\"display: none;\"><div style=\"display: table-cell;\"></div><div style=\"display: table-cell;\">muted&nbsp;=&nbsp;False,</div></div><div class=\"1019\" style=\"display: none;\"><div style=\"display: table-cell;\"></div><div style=\"display: table-cell;\">muted_glyph&nbsp;=&nbsp;None,</div></div><div class=\"1019\" style=\"display: none;\"><div style=\"display: table-cell;\"></div><div style=\"display: table-cell;\">name&nbsp;=&nbsp;None,</div></div><div class=\"1019\" style=\"display: none;\"><div style=\"display: table-cell;\"></div><div style=\"display: table-cell;\">nonselection_glyph&nbsp;=&nbsp;'auto',</div></div><div class=\"1019\" style=\"display: none;\"><div style=\"display: table-cell;\"></div><div style=\"display: table-cell;\">selection_glyph&nbsp;=&nbsp;'auto',</div></div><div class=\"1019\" style=\"display: none;\"><div style=\"display: table-cell;\"></div><div style=\"display: table-cell;\">subscribed_events&nbsp;=&nbsp;[],</div></div><div class=\"1019\" style=\"display: none;\"><div style=\"display: table-cell;\"></div><div style=\"display: table-cell;\">tags&nbsp;=&nbsp;[],</div></div><div class=\"1019\" style=\"display: none;\"><div style=\"display: table-cell;\"></div><div style=\"display: table-cell;\">view&nbsp;=&nbsp;CDSView(id='1018', ...),</div></div><div class=\"1019\" style=\"display: none;\"><div style=\"display: table-cell;\"></div><div style=\"display: table-cell;\">visible&nbsp;=&nbsp;True,</div></div><div class=\"1019\" style=\"display: none;\"><div style=\"display: table-cell;\"></div><div style=\"display: table-cell;\">x_range_name&nbsp;=&nbsp;'default',</div></div><div class=\"1019\" style=\"display: none;\"><div style=\"display: table-cell;\"></div><div style=\"display: table-cell;\">y_range_name&nbsp;=&nbsp;'default')</div></div></div>\n",
       "<script>\n",
       "(function() {\n",
       "  var expanded = false;\n",
       "  var ellipsis = document.getElementById(\"1020\");\n",
       "  ellipsis.addEventListener(\"click\", function() {\n",
       "    var rows = document.getElementsByClassName(\"1019\");\n",
       "    for (var i = 0; i < rows.length; i++) {\n",
       "      var el = rows[i];\n",
       "      el.style.display = expanded ? \"none\" : \"table-row\";\n",
       "    }\n",
       "    ellipsis.innerHTML = expanded ? \"&hellip;)\" : \"&lsaquo;&lsaquo;&lsaquo;\";\n",
       "    expanded = !expanded;\n",
       "  });\n",
       "})();\n",
       "</script>\n"
      ],
      "text/plain": [
       "GlyphRenderer(id='1017', ...)"
      ]
     },
     "execution_count": 7,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "xdr = Range1d(start=sprint.MetersBack.max()+2, end=0)  # +2 is for padding\n",
    "ydr = DataRange1d(range_padding=0.05)  \n",
    "\n",
    "plot = Plot(x_range=xdr, y_range=ydr, **plot_options)\n",
    "plot.title.text = \"Usain Bolt vs. 116 years of Olympic sprinters\"\n",
    "plot.add_glyph(source, medal_glyph)\n",
    "plot.add_glyph(source, athlete_glyph)\n",
    "plot.add_glyph(no_olympics_glyph)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "\n",
       "\n",
       "\n",
       "\n",
       "\n",
       "\n",
       "  <div class=\"bk-root\" id=\"594ba714-7580-47ab-a836-d84ff6b0e134\" data-root-id=\"1008\"></div>\n"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "application/javascript": [
       "(function(root) {\n",
       "  function embed_document(root) {\n",
       "    \n",
       "  var docs_json = {\"fd4154a7-7367-4a20-a41e-d5ab9c390771\":{\"roots\":{\"references\":[{\"attributes\":{\"outline_line_color\":null,\"plot_height\":480,\"plot_width\":800,\"renderers\":[{\"id\":\"1012\"},{\"id\":\"1014\"},{\"id\":\"1017\"}],\"title\":{\"id\":\"1011\"},\"toolbar\":{\"id\":\"1023\"},\"toolbar_location\":null,\"x_range\":{\"id\":\"1006\"},\"x_scale\":{\"id\":\"1024\"},\"y_range\":{\"id\":\"1007\"},\"y_scale\":{\"id\":\"1021\"}},\"id\":\"1008\",\"type\":\"Plot\"},{\"attributes\":{},\"id\":\"1021\",\"type\":\"LinearScale\"},{\"attributes\":{\"data_source\":{\"id\":\"1002\"},\"glyph\":{\"id\":\"1004\"},\"hover_glyph\":null,\"muted_glyph\":null,\"view\":{\"id\":\"1015\"}},\"id\":\"1014\",\"type\":\"GlyphRenderer\"},{\"attributes\":{\"range_padding\":0.05},\"id\":\"1007\",\"type\":\"DataRange1d\"},{\"attributes\":{\"source\":{\"id\":\"1002\"}},\"id\":\"1013\",\"type\":\"CDSView\"},{\"attributes\":{\"data\":{\"Abbrev\":[\"JAM\",\"JAM\",\"USA\",\"JAM\",\"TRI\",\"USA\",\"USA\",\"POR\",\"USA\",\"USA\",\"TRI\",\"BAR\",\"CAN\",\"NAM\",\"TRI\",\"GBR\",\"NAM\",\"USA\",\"USA\",\"GBR\",\"USA\",\"USA\",\"USA\",\"CAN\",\"GBR\",\"CUB\",\"BUL\",\"TRI\",\"JAM\",\"URS\",\"URS\",\"USA\",\"JAM\",\"USA\",\"JAM\",\"USA\",\"USA\",\"CUB\",\"CAN\",\"EUA\",\"USA\",\"GBR\",\"USA\",\"USA\",\"AUS\",\"USA\",\"JAM\",\"GBR\",\"USA\",\"USA\",\"PAN\",\"USA\",\"USA\",\"NED\",\"USA\",\"USA\",\"GER\",\"CAN\",\"GBR\",\"GER\",\"GBR\",\"USA\",\"NZL\",\"USA\",\"USA\",\"GBR\",\"USA\",\"USA\",\"USA\",\"RSA\",\"USA\",\"CAN\",\"USA\",\"USA\",\"AUS\",\"USA\",\"USA\",\"USA\",\"USA\",\"USA\",\"AUS\",\"USA\",\"GER\",\"HUN\",\"USA\"],\"Country\":[\"Jamaica\",\"Jamaica\",\"United States\",\"Jamaica\",\"Trinidad and Tobago\",\"United States\",\"United States\",\"Portugal\",\"United States\",\"United States\",\"Trinidad and Tobago\",\"Barbados\",\"Canada\",\"Namibia\",\"Trinidad and Tobago\",\"Britain\",\"Namibia\",\"United States\",\"United States\",\"Britain\",\"United States\",\"United States\",\"United States\",\"Canada\",\"Britain\",\"Cuba\",\"Bulgaria\",\"Trinidad and Tobago\",\"Jamaica\",\"Soviet Union\",\"Soviet Union\",\"United States\",\"Jamaica\",\"United States\",\"Jamaica\",\"United States\",\"United States\",\"Cuba\",\"Canada\",\"United Team of Germany\",\"United States\",\"Britain\",\"United States\",\"United States\",\"Australia\",\"United States\",\"Jamaica\",\"Britain\",\"United States\",\"United States\",\"Panama\",\"United States\",\"United States\",\"Netherlands\",\"United States\",\"United States\",\"Germany\",\"Canada\",\"Britain\",\"Germany\",\"Britain\",\"United States\",\"New Zealand\",\"United States\",\"United States\",\"Britain\",\"United States\",\"United States\",\"United States\",\"South Africa\",\"United States\",\"Canada\",\"United States\",\"United States\",\"Australia\",\"United States\",\"United States\",\"United States\",\"United States\",\"United States\",\"Australia\",\"United States\",\"Germany\",\"Hungary\",\"United States\"],\"Medal\":[\"gold\",\"silver\",\"bronze\",\"gold\",\"silver\",\"bronze\",\"gold\",\"silver\",\"bronze\",\"gold\",\"silver\",\"bronze\",\"gold\",\"silver\",\"bronze\",\"gold\",\"silver\",\"bronze\",\"gold\",\"silver\",\"bronze\",\"gold\",\"silver\",\"bronze\",\"gold\",\"silver\",\"bronze\",\"gold\",\"silver\",\"bronze\",\"gold\",\"silver\",\"bronze\",\"gold\",\"silver\",\"bronze\",\"gold\",\"silver\",\"bronze\",\"gold\",\"silver\",\"bronze\",\"gold\",\"silver\",\"bronze\",\"gold\",\"silver\",\"bronze\",\"gold\",\"silver\",\"bronze\",\"gold\",\"silver\",\"bronze\",\"gold\",\"silver\",\"bronze\",\"gold\",\"silver\",\"bronze\",\"gold\",\"silver\",\"bronze\",\"gold\",\"silver\",\"bronze\",\"gold\",\"silver\",\"bronze\",\"gold\",\"silver\",\"bronze\",\"gold\",\"silver\",\"bronze\",\"gold\",\"silver\",\"bronze\",\"gold\",\"silver\",\"bronze\",\"gold\",\"silver\",\"bronze\",\"bronze\"],\"MedalFill\":[\"#efcf6d\",\"#cccccc\",\"#c59e8a\",\"#efcf6d\",\"#cccccc\",\"#c59e8a\",\"#efcf6d\",\"#cccccc\",\"#c59e8a\",\"#efcf6d\",\"#cccccc\",\"#c59e8a\",\"#efcf6d\",\"#cccccc\",\"#c59e8a\",\"#efcf6d\",\"#cccccc\",\"#c59e8a\",\"#efcf6d\",\"#cccccc\",\"#c59e8a\",\"#efcf6d\",\"#cccccc\",\"#c59e8a\",\"#efcf6d\",\"#cccccc\",\"#c59e8a\",\"#efcf6d\",\"#cccccc\",\"#c59e8a\",\"#efcf6d\",\"#cccccc\",\"#c59e8a\",\"#efcf6d\",\"#cccccc\",\"#c59e8a\",\"#efcf6d\",\"#cccccc\",\"#c59e8a\",\"#efcf6d\",\"#cccccc\",\"#c59e8a\",\"#efcf6d\",\"#cccccc\",\"#c59e8a\",\"#efcf6d\",\"#cccccc\",\"#c59e8a\",\"#efcf6d\",\"#cccccc\",\"#c59e8a\",\"#efcf6d\",\"#cccccc\",\"#c59e8a\",\"#efcf6d\",\"#cccccc\",\"#c59e8a\",\"#efcf6d\",\"#cccccc\",\"#c59e8a\",\"#efcf6d\",\"#cccccc\",\"#c59e8a\",\"#efcf6d\",\"#cccccc\",\"#c59e8a\",\"#efcf6d\",\"#cccccc\",\"#c59e8a\",\"#efcf6d\",\"#cccccc\",\"#c59e8a\",\"#efcf6d\",\"#cccccc\",\"#c59e8a\",\"#efcf6d\",\"#cccccc\",\"#c59e8a\",\"#efcf6d\",\"#cccccc\",\"#c59e8a\",\"#efcf6d\",\"#cccccc\",\"#c59e8a\",\"#c59e8a\"],\"MedalLine\":[\"#c8a850\",\"#b0b0b1\",\"#98715d\",\"#c8a850\",\"#b0b0b1\",\"#98715d\",\"#c8a850\",\"#b0b0b1\",\"#98715d\",\"#c8a850\",\"#b0b0b1\",\"#98715d\",\"#c8a850\",\"#b0b0b1\",\"#98715d\",\"#c8a850\",\"#b0b0b1\",\"#98715d\",\"#c8a850\",\"#b0b0b1\",\"#98715d\",\"#c8a850\",\"#b0b0b1\",\"#98715d\",\"#c8a850\",\"#b0b0b1\",\"#98715d\",\"#c8a850\",\"#b0b0b1\",\"#98715d\",\"#c8a850\",\"#b0b0b1\",\"#98715d\",\"#c8a850\",\"#b0b0b1\",\"#98715d\",\"#c8a850\",\"#b0b0b1\",\"#98715d\",\"#c8a850\",\"#b0b0b1\",\"#98715d\",\"#c8a850\",\"#b0b0b1\",\"#98715d\",\"#c8a850\",\"#b0b0b1\",\"#98715d\",\"#c8a850\",\"#b0b0b1\",\"#98715d\",\"#c8a850\",\"#b0b0b1\",\"#98715d\",\"#c8a850\",\"#b0b0b1\",\"#98715d\",\"#c8a850\",\"#b0b0b1\",\"#98715d\",\"#c8a850\",\"#b0b0b1\",\"#98715d\",\"#c8a850\",\"#b0b0b1\",\"#98715d\",\"#c8a850\",\"#b0b0b1\",\"#98715d\",\"#c8a850\",\"#b0b0b1\",\"#98715d\",\"#c8a850\",\"#b0b0b1\",\"#98715d\",\"#c8a850\",\"#b0b0b1\",\"#98715d\",\"#c8a850\",\"#b0b0b1\",\"#98715d\",\"#c8a850\",\"#b0b0b1\",\"#98715d\",\"#98715d\"],\"MetersBack\":{\"__ndarray__\":\"AAAAAAAAAAD2OrETO7HzPzSH3n8tJvo/+LRJIXLQ4z+4oVs2BggFQE37LXJ6mgZApWIklDbeAUD2G7ItSKkCQBFzDHDwcwNAEXMMcPBzA0A7zUkgLtQMQMAp1V2sVRBA/rQrUbsSAUC4oVs2BggFQMdFF1100QVAuPaQy4qBCkC79onqQSMPQMAp1V2sVRBAV4wxxhhjB0A2Q3IHJ0gLQDvNSSAu1AxAO81JIC7UDEDqKu9YevsVQHzx4sWLFxdALDgfg/MxGEAsOB+D8zEYQO/PDHRHQh1AQ4IAOfAYEUCybdu2bdsRQGZfWb1LHhRAZl9ZvUseFED0/////9MXQEJPwDgDGxtAX4SfXIi6CUDAKdVdrFUQQFLnrrBHehFAiJmZmZmZDUBQWlpaWloWQFBaWlpaWhZAUFpaWlpaFkBQWlpaWloWQCQ5L4v4BBpAHkmSJEmSIEAeSZIkSZIgQN+t7HNITSJA1Imd2ImdHUDUiZ3YiZ0dQNSJndiJnR1AJDkvi/gEGkDUiZ3YiZ0dQNSJndiJnR1AJDkvi/gEGkDUiZ3YiZ0dQB5JkiRJkiBAJDkvi/gEGkAkOS+L+AQaQNSJndiJnR1AqKqqqqqqJUDUtJ8sgU0nQNS0nyyBTSdA363sc0hNIkCoqqqqqqolQNS0nyyBTSdAqKqqqqqqJUDUtJ8sgU0nQNS0nyyBTSdAqKqqqqqqJUDUtJ8sgU0nQNS0nyyBTSdAqKqqqqqqJUDnoosuuugoQOeiiy666ChAiSRJkiQJLECa6xjFuY4tQJrrGMW5ji1A56KLLrroKECJJEmSJAksQIkkSZIkCSxA56KLLrroKEDBZ91ginwqQIkkSZIkCSxA/f////+/M0DYvE9xyRA1QCNJkiRJkjdAI0mSJEmSN0A=\",\"dtype\":\"float64\",\"shape\":[85]},\"Name\":[\"Usain Bolt\",\"Yohan Blake\",\"Justin Gatlin\",\"Usain Bolt\",\"Richard Thompson\",\"Walter Dix\",\"Justin Gatlin\",\"Francis Obikwelu\",\"Maurice Greene\",\"Maurice Greene\",\"Ato Boldon\",\"Obadele Thompson\",\"Donovan Bailey\",\"Frankie Fredericks\",\"Ato Boldon\",\"Linford Christie\",\"Frankie Fredericks\",\"Dennis Mitchell\",\"Carl Lewis\",\"Linford Christie\",\"Calvin Smith\",\"Carl Lewis\",\"Sam Graddy\",\"Ben Johnson\",\"Allan Wells\",\"Silvio Leonard Tartabull\",\"Petar Petrov\",\"Hasely Crawford\",\"Donald Quarrie\",\"Valery Borzov\",\"Valery Borzov\",\"Robert Taylor\",\"Lennox Miller\",\"Jim Hines\",\"Lennox Miller\",\"Charles Greene\",\"Bob Hayes\",\"Enrique Figuerola Camue\",\"Harry Jerome\",\"Armin Hary\",\"David Sime\",\"Peter Radford\",\"Bobby Joe Morrow\",\"W. Thane Baker\",\"Hector Hogan\",\"Lindy Remigino\",\"Herbert McKenley\",\"E. McDonald Bailey\",\"W. Harrison Dillard\",\"H. Norwood \\\"Barney\\\" Ewell\",\"Lloyd LaBeach\",\"Jesse Owens\",\"Ralph Metcalfe\",\"Martinus Osendarp\",\"T. \\\"Eddie\\\" Tolan\",\"Ralph Metcalfe\",\"Arthur Jonath\",\"Percy Williams\",\"John \\\"Jack\\\" London\",\"Georg Lammers\",\"Harold Abrahams\",\"Jackson Scholz\",\"Arthur Porritt\",\"Charles Paddock\",\"Morris Kirksey\",\"Harry Edward\",\"Ralph Craig\",\"Alvah Meyer\",\"Donald Lippincott\",\"Reginald Walker\",\"James Rector\",\"Robert Kerr\",\"Charles \\\"Archie\\\" Hahn\",\"Fay Moulton\",\"Nigel Barker\",\"Charles \\\"Archie\\\" Hahn\",\"Nathaniel Cartmell\",\"Bill Hogenson\",\"Frank Jarvis\",\"J. Walter Tewksbury\",\"Stanley Rowley\",\"Thomas Burke\",\"Fritz Hofmann\",\"Alojz Sokol\",\"Francis Lane\"],\"SelectedName\":[\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"Carl Lewis\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"Jim Hines\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"Jesse Owens\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"Thomas Burke\",\"\",\"\",\"\"],\"Speed\":{\"__ndarray__\":\"D9dd9bfEJEA1SIM0SIMkQNPZ5YvTbSRAEB0COMyjJEBnpfVH8jgkQGugzZx/LiRAUs36M/hNJEDfP/mgskgkQPb3DMpvQyRA9vcMym9DJEBSQDMIIAUkQP8yCgKa6yNAUwg0hUBTJEBnpfVH8jgkQN7OkJi3MyRA9dmM748UJEB3uNvKx/UjQP8yCgKa6yNAKaWUUkopJEBoIvL9Zw8kQFJAMwggBSRAUkAzCCAFJEDuitWqiKAjQBw5cuTIkSNAg/MxOB+DI0CD8zE4H4MjQLW+ZrHQPyNAA1ahlXbhI0B2Xdd1XdcjQKh2L0ZPuSNAqHYvRk+5I0AAAAAAAIgjQBMU9wNwXCNAhZ9ciLoZJED/MgoCmusjQFfYI71o3CNAAAAAAAAAJECcm5ubm5sjQJybm5ubmyNAnJubm5ubI0Ccm5ubm5sjQAxNd7DfaiNAwzAMwzAMI0DDMAzDMAwjQO1zSE0w3iJAsRM7sRM7I0CxEzuxEzsjQLETO7ETOyNADE13sN9qI0CxEzuxEzsjQLETO7ETOyNADE13sN9qI0CxEzuxEzsjQMMwDMMwDCNADE13sN9qI0AMTXew32ojQLETO7ETOyNATGgvob2EIkCUJbBpP1kiQJQlsGk/WSJA7XNITTDeIkBMaC+hvYQiQJQlsGk/WSJATGgvob2EIkCUJbBpP1kiQJQlsGk/WSJATGgvob2EIkCUJbBpP1kiQJQlsGk/WSJATGgvob2EIkAvuuiiiy4iQC+66KKLLiJAt23btm3bIUCTLxs0+bIhQJMvGzT5siFAL7rooosuIkC3bdu2bdshQLdt27Zt2yFAL7rooosuIkBKIC7UnAQiQLdt27Zt2yFAq6qqqqqqIEAebd6nuGQgQPD7vu/7vh9A8Pu+7/u+H0A=\",\"dtype\":\"float64\",\"shape\":[85]},\"Time\":{\"__ndarray__\":\"w/UoXI9CI0AAAAAAAIAjQBSuR+F6lCNA4XoUrkdhI0BI4XoUrscjQFK4HoXr0SNAMzMzMzOzI0C4HoXrUbgjQD0K16NwvSNAPQrXo3C9I0B7FK5H4fojQBSuR+F6FCRArkfhehSuI0BI4XoUrscjQM3MzMzMzCNA7FG4HoXrI0AK16NwPQokQBSuR+F6FCRA16NwPQrXI0BxPQrXo/AjQHsUrkfh+iNAexSuR+H6I0DhehSuR2EkQHE9CtejcCRAAAAAAACAJEAAAAAAAIAkQEjhehSuxyRAH4XrUbgeJEApXI/C9SgkQEjhehSuRyRASOF6FK5HJEB7FK5H4XokQClcj8L1qCRAZmZmZmbmI0AUrkfhehQkQKRwPQrXIyRAAAAAAAAAJEBmZmZmZmYkQGZmZmZmZiRAZmZmZmZmJEBmZmZmZmYkQJqZmZmZmSRAAAAAAAAAJUAAAAAAAAAlQDMzMzMzMyVAzczMzMzMJEDNzMzMzMwkQM3MzMzMzCRAmpmZmZmZJEDNzMzMzMwkQM3MzMzMzCRAmpmZmZmZJEDNzMzMzMwkQAAAAAAAACVAmpmZmZmZJECamZmZmZkkQM3MzMzMzCRAmpmZmZmZJUDNzMzMzMwlQM3MzMzMzCVAMzMzMzMzJUCamZmZmZklQM3MzMzMzCVAmpmZmZmZJUDNzMzMzMwlQM3MzMzMzCVAmpmZmZmZJUDNzMzMzMwlQM3MzMzMzCVAmpmZmZmZJUAAAAAAAAAmQAAAAAAAACZAZmZmZmZmJkCamZmZmZkmQJqZmZmZmSZAAAAAAAAAJkBmZmZmZmYmQGZmZmZmZiZAAAAAAAAAJkAzMzMzMzMmQGZmZmZmZiZAAAAAAAAAKEBmZmZmZmYoQDMzMzMzMylAMzMzMzMzKUA=\",\"dtype\":\"float64\",\"shape\":[85]},\"Year\":[2012,2012,2012,2008,2008,2008,2004,2004,2004,2000,2000,2000,1996,1996,1996,1992,1992,1992,1988,1988,1988,1984,1984,1984,1980,1980,1980,1976,1976,1976,1972,1972,1972,1968,1968,1968,1964,1964,1964,1960,1960,1960,1956,1956,1956,1952,1952,1952,1948,1948,1948,1936,1936,1936,1932,1932,1932,1928,1928,1928,1924,1924,1924,1920,1920,1920,1912,1912,1912,1908,1908,1908,1906,1906,1906,1904,1904,1904,1900,1900,1900,1896,1896,1896,1896],\"index\":[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84]},\"selected\":{\"id\":\"1025\"},\"selection_policy\":{\"id\":\"1026\"}},\"id\":\"1002\",\"type\":\"ColumnDataSource\"},{\"attributes\":{\"data\":{},\"selected\":{\"id\":\"1027\"},\"selection_policy\":{\"id\":\"1028\"}},\"id\":\"1016\",\"type\":\"ColumnDataSource\"},{\"attributes\":{\"fill_alpha\":{\"value\":0.5},\"fill_color\":{\"field\":\"MedalFill\"},\"line_color\":{\"field\":\"MedalLine\"},\"size\":{\"units\":\"screen\",\"value\":10},\"x\":{\"field\":\"MetersBack\"},\"y\":{\"field\":\"Year\"}},\"id\":\"1003\",\"type\":\"Circle\"},{\"attributes\":{},\"id\":\"1025\",\"type\":\"Selection\"},{\"attributes\":{\"source\":{\"id\":\"1016\"}},\"id\":\"1018\",\"type\":\"CDSView\"},{\"attributes\":{},\"id\":\"1026\",\"type\":\"UnionRenderers\"},{\"attributes\":{},\"id\":\"1024\",\"type\":\"LinearScale\"},{\"attributes\":{\"text\":{\"value\":\"No Olympics in 1940 or 1944\"},\"text_align\":\"center\",\"text_baseline\":\"middle\",\"text_color\":{\"value\":\"silver\"},\"text_font_size\":{\"value\":\"9pt\"},\"text_font_style\":\"italic\",\"x\":{\"value\":7.5},\"y\":{\"value\":1942}},\"id\":\"1005\",\"type\":\"Text\"},{\"attributes\":{\"text\":{\"field\":\"SelectedName\"},\"text_baseline\":\"middle\",\"text_font_size\":{\"value\":\"9pt\"},\"x\":{\"field\":\"MetersBack\"},\"x_offset\":{\"value\":10},\"y\":{\"field\":\"Year\"}},\"id\":\"1004\",\"type\":\"Text\"},{\"attributes\":{\"source\":{\"id\":\"1002\"}},\"id\":\"1015\",\"type\":\"CDSView\"},{\"attributes\":{\"active_drag\":\"auto\",\"active_inspect\":\"auto\",\"active_multi\":null,\"active_scroll\":\"auto\",\"active_tap\":\"auto\"},\"id\":\"1023\",\"type\":\"Toolbar\"},{\"attributes\":{},\"id\":\"1028\",\"type\":\"UnionRenderers\"},{\"attributes\":{\"text\":\"Usain Bolt vs. 116 years of Olympic sprinters\"},\"id\":\"1011\",\"type\":\"Title\"},{\"attributes\":{},\"id\":\"1027\",\"type\":\"Selection\"},{\"attributes\":{\"data_source\":{\"id\":\"1016\"},\"glyph\":{\"id\":\"1005\"},\"hover_glyph\":null,\"muted_glyph\":null,\"view\":{\"id\":\"1018\"}},\"id\":\"1017\",\"type\":\"GlyphRenderer\"},{\"attributes\":{\"data_source\":{\"id\":\"1002\"},\"glyph\":{\"id\":\"1003\"},\"hover_glyph\":null,\"muted_glyph\":null,\"view\":{\"id\":\"1013\"}},\"id\":\"1012\",\"type\":\"GlyphRenderer\"},{\"attributes\":{\"end\":0,\"start\":25.571428571428566},\"id\":\"1006\",\"type\":\"Range1d\"}],\"root_ids\":[\"1008\"]},\"title\":\"Bokeh Application\",\"version\":\"2.0.1\"}};\n",
       "  var render_items = [{\"docid\":\"fd4154a7-7367-4a20-a41e-d5ab9c390771\",\"root_ids\":[\"1008\"],\"roots\":{\"1008\":\"594ba714-7580-47ab-a836-d84ff6b0e134\"}}];\n",
       "  root.Bokeh.embed.embed_items_notebook(docs_json, render_items);\n",
       "\n",
       "  }\n",
       "  if (root.Bokeh !== undefined) {\n",
       "    embed_document(root);\n",
       "  } else {\n",
       "    var attempts = 0;\n",
       "    var timer = setInterval(function(root) {\n",
       "      if (root.Bokeh !== undefined) {\n",
       "        clearInterval(timer);\n",
       "        embed_document(root);\n",
       "      } else {\n",
       "        attempts++;\n",
       "        if (attempts > 100) {\n",
       "          clearInterval(timer);\n",
       "          console.log(\"Bokeh: ERROR: Unable to run BokehJS code because BokehJS library is missing\");\n",
       "        }\n",
       "      }\n",
       "    }, 10, root)\n",
       "  }\n",
       "})(window);"
      ],
      "application/vnd.bokehjs_exec.v0+json": ""
     },
     "metadata": {
      "application/vnd.bokehjs_exec.v0+json": {
       "id": "1008"
      }
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "show(plot)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Adding Axes and Grids\n",
    "\n",
    "Next we add in models for the `Axis` and `Grids` that we would like to see. Since we want to exert more control over the appearance, we can choose specific tickers for the axes models to use (`SingleIntervalTicker` in this case). We add these guides to the plot using the `plot.add_layout` method. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [],
   "source": [
    "from bokeh.models import Grid, LinearAxis, SingleIntervalTicker"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div style=\"display: table;\"><div style=\"display: table-row;\"><div style=\"display: table-cell;\"><b title=\"bokeh.models.renderers.GlyphRenderer\">GlyphRenderer</b>(</div><div style=\"display: table-cell;\">id&nbsp;=&nbsp;'1096', <span id=\"1099\" style=\"cursor: pointer;\">&hellip;)</span></div></div><div class=\"1098\" style=\"display: none;\"><div style=\"display: table-cell;\"></div><div style=\"display: table-cell;\">data_source&nbsp;=&nbsp;ColumnDataSource(id='1095', ...),</div></div><div class=\"1098\" style=\"display: none;\"><div style=\"display: table-cell;\"></div><div style=\"display: table-cell;\">glyph&nbsp;=&nbsp;Text(id='1005', ...),</div></div><div class=\"1098\" style=\"display: none;\"><div style=\"display: table-cell;\"></div><div style=\"display: table-cell;\">hover_glyph&nbsp;=&nbsp;None,</div></div><div class=\"1098\" style=\"display: none;\"><div style=\"display: table-cell;\"></div><div style=\"display: table-cell;\">js_event_callbacks&nbsp;=&nbsp;{},</div></div><div class=\"1098\" style=\"display: none;\"><div style=\"display: table-cell;\"></div><div style=\"display: table-cell;\">js_property_callbacks&nbsp;=&nbsp;{},</div></div><div class=\"1098\" style=\"display: none;\"><div style=\"display: table-cell;\"></div><div style=\"display: table-cell;\">level&nbsp;=&nbsp;'glyph',</div></div><div class=\"1098\" style=\"display: none;\"><div style=\"display: table-cell;\"></div><div style=\"display: table-cell;\">muted&nbsp;=&nbsp;False,</div></div><div class=\"1098\" style=\"display: none;\"><div style=\"display: table-cell;\"></div><div style=\"display: table-cell;\">muted_glyph&nbsp;=&nbsp;None,</div></div><div class=\"1098\" style=\"display: none;\"><div style=\"display: table-cell;\"></div><div style=\"display: table-cell;\">name&nbsp;=&nbsp;None,</div></div><div class=\"1098\" style=\"display: none;\"><div style=\"display: table-cell;\"></div><div style=\"display: table-cell;\">nonselection_glyph&nbsp;=&nbsp;'auto',</div></div><div class=\"1098\" style=\"display: none;\"><div style=\"display: table-cell;\"></div><div style=\"display: table-cell;\">selection_glyph&nbsp;=&nbsp;'auto',</div></div><div class=\"1098\" style=\"display: none;\"><div style=\"display: table-cell;\"></div><div style=\"display: table-cell;\">subscribed_events&nbsp;=&nbsp;[],</div></div><div class=\"1098\" style=\"display: none;\"><div style=\"display: table-cell;\"></div><div style=\"display: table-cell;\">tags&nbsp;=&nbsp;[],</div></div><div class=\"1098\" style=\"display: none;\"><div style=\"display: table-cell;\"></div><div style=\"display: table-cell;\">view&nbsp;=&nbsp;CDSView(id='1097', ...),</div></div><div class=\"1098\" style=\"display: none;\"><div style=\"display: table-cell;\"></div><div style=\"display: table-cell;\">visible&nbsp;=&nbsp;True,</div></div><div class=\"1098\" style=\"display: none;\"><div style=\"display: table-cell;\"></div><div style=\"display: table-cell;\">x_range_name&nbsp;=&nbsp;'default',</div></div><div class=\"1098\" style=\"display: none;\"><div style=\"display: table-cell;\"></div><div style=\"display: table-cell;\">y_range_name&nbsp;=&nbsp;'default')</div></div></div>\n",
       "<script>\n",
       "(function() {\n",
       "  var expanded = false;\n",
       "  var ellipsis = document.getElementById(\"1099\");\n",
       "  ellipsis.addEventListener(\"click\", function() {\n",
       "    var rows = document.getElementsByClassName(\"1098\");\n",
       "    for (var i = 0; i < rows.length; i++) {\n",
       "      var el = rows[i];\n",
       "      el.style.display = expanded ? \"none\" : \"table-row\";\n",
       "    }\n",
       "    ellipsis.innerHTML = expanded ? \"&hellip;)\" : \"&lsaquo;&lsaquo;&lsaquo;\";\n",
       "    expanded = !expanded;\n",
       "  });\n",
       "})();\n",
       "</script>\n"
      ],
      "text/plain": [
       "GlyphRenderer(id='1096', ...)"
      ]
     },
     "execution_count": 10,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "xdr = Range1d(start=sprint.MetersBack.max()+2, end=0)  # +2 is for padding\n",
    "ydr = DataRange1d(range_padding=0.05)  \n",
    "\n",
    "plot = Plot(x_range=xdr, y_range=ydr, **plot_options)\n",
    "plot.title.text = \"Usain Bolt vs. 116 years of Olympic sprinters\"\n",
    "plot.add_glyph(source, medal_glyph)\n",
    "plot.add_glyph(source, athlete_glyph)\n",
    "plot.add_glyph(no_olympics_glyph)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [],
   "source": [
    "xticker = SingleIntervalTicker(interval=5, num_minor_ticks=0)\n",
    "xaxis = LinearAxis(ticker=xticker, axis_line_color=None, major_tick_line_color=None,\n",
    "                   axis_label=\"Meters behind 2012 Bolt\", axis_label_text_font_size=\"10pt\", \n",
    "                   axis_label_text_font_style=\"bold\")\n",
    "plot.add_layout(xaxis, \"below\")\n",
    "\n",
    "xgrid = Grid(dimension=0, ticker=xaxis.ticker, grid_line_dash=\"dashed\")\n",
    "plot.add_layout(xgrid)\n",
    "\n",
    "yticker = SingleIntervalTicker(interval=12, num_minor_ticks=0)\n",
    "yaxis = LinearAxis(ticker=yticker, major_tick_in=-5, major_tick_out=10)\n",
    "plot.add_layout(yaxis, \"right\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "\n",
       "\n",
       "\n",
       "\n",
       "\n",
       "\n",
       "  <div class=\"bk-root\" id=\"c886dffc-9e9c-4271-b1ad-e4cfdd9f03c6\" data-root-id=\"1087\"></div>\n"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "application/javascript": [
       "(function(root) {\n",
       "  function embed_document(root) {\n",
       "    \n",
       "  var docs_json = {\"d1eab26d-e515-4c1e-a032-d6fc023cc93e\":{\"roots\":{\"references\":[{\"attributes\":{\"below\":[{\"id\":\"1101\"}],\"center\":[{\"id\":\"1103\"}],\"outline_line_color\":null,\"plot_height\":480,\"plot_width\":800,\"renderers\":[{\"id\":\"1091\"},{\"id\":\"1093\"},{\"id\":\"1096\"}],\"right\":[{\"id\":\"1105\"}],\"title\":{\"id\":\"1090\"},\"toolbar\":{\"id\":\"1117\"},\"toolbar_location\":null,\"x_range\":{\"id\":\"1085\"},\"x_scale\":{\"id\":\"1118\"},\"y_range\":{\"id\":\"1086\"},\"y_scale\":{\"id\":\"1115\"}},\"id\":\"1087\",\"type\":\"Plot\"},{\"attributes\":{},\"id\":\"1119\",\"type\":\"BasicTickFormatter\"},{\"attributes\":{\"data_source\":{\"id\":\"1095\"},\"glyph\":{\"id\":\"1005\"},\"hover_glyph\":null,\"muted_glyph\":null,\"view\":{\"id\":\"1097\"}},\"id\":\"1096\",\"type\":\"GlyphRenderer\"},{\"attributes\":{\"data\":{\"Abbrev\":[\"JAM\",\"JAM\",\"USA\",\"JAM\",\"TRI\",\"USA\",\"USA\",\"POR\",\"USA\",\"USA\",\"TRI\",\"BAR\",\"CAN\",\"NAM\",\"TRI\",\"GBR\",\"NAM\",\"USA\",\"USA\",\"GBR\",\"USA\",\"USA\",\"USA\",\"CAN\",\"GBR\",\"CUB\",\"BUL\",\"TRI\",\"JAM\",\"URS\",\"URS\",\"USA\",\"JAM\",\"USA\",\"JAM\",\"USA\",\"USA\",\"CUB\",\"CAN\",\"EUA\",\"USA\",\"GBR\",\"USA\",\"USA\",\"AUS\",\"USA\",\"JAM\",\"GBR\",\"USA\",\"USA\",\"PAN\",\"USA\",\"USA\",\"NED\",\"USA\",\"USA\",\"GER\",\"CAN\",\"GBR\",\"GER\",\"GBR\",\"USA\",\"NZL\",\"USA\",\"USA\",\"GBR\",\"USA\",\"USA\",\"USA\",\"RSA\",\"USA\",\"CAN\",\"USA\",\"USA\",\"AUS\",\"USA\",\"USA\",\"USA\",\"USA\",\"USA\",\"AUS\",\"USA\",\"GER\",\"HUN\",\"USA\"],\"Country\":[\"Jamaica\",\"Jamaica\",\"United States\",\"Jamaica\",\"Trinidad and Tobago\",\"United States\",\"United States\",\"Portugal\",\"United States\",\"United States\",\"Trinidad and Tobago\",\"Barbados\",\"Canada\",\"Namibia\",\"Trinidad and Tobago\",\"Britain\",\"Namibia\",\"United States\",\"United States\",\"Britain\",\"United States\",\"United States\",\"United States\",\"Canada\",\"Britain\",\"Cuba\",\"Bulgaria\",\"Trinidad and Tobago\",\"Jamaica\",\"Soviet Union\",\"Soviet Union\",\"United States\",\"Jamaica\",\"United States\",\"Jamaica\",\"United States\",\"United States\",\"Cuba\",\"Canada\",\"United Team of Germany\",\"United States\",\"Britain\",\"United States\",\"United States\",\"Australia\",\"United States\",\"Jamaica\",\"Britain\",\"United States\",\"United States\",\"Panama\",\"United States\",\"United States\",\"Netherlands\",\"United States\",\"United States\",\"Germany\",\"Canada\",\"Britain\",\"Germany\",\"Britain\",\"United States\",\"New Zealand\",\"United States\",\"United States\",\"Britain\",\"United States\",\"United States\",\"United States\",\"South Africa\",\"United States\",\"Canada\",\"United States\",\"United States\",\"Australia\",\"United States\",\"United States\",\"United States\",\"United States\",\"United States\",\"Australia\",\"United States\",\"Germany\",\"Hungary\",\"United States\"],\"Medal\":[\"gold\",\"silver\",\"bronze\",\"gold\",\"silver\",\"bronze\",\"gold\",\"silver\",\"bronze\",\"gold\",\"silver\",\"bronze\",\"gold\",\"silver\",\"bronze\",\"gold\",\"silver\",\"bronze\",\"gold\",\"silver\",\"bronze\",\"gold\",\"silver\",\"bronze\",\"gold\",\"silver\",\"bronze\",\"gold\",\"silver\",\"bronze\",\"gold\",\"silver\",\"bronze\",\"gold\",\"silver\",\"bronze\",\"gold\",\"silver\",\"bronze\",\"gold\",\"silver\",\"bronze\",\"gold\",\"silver\",\"bronze\",\"gold\",\"silver\",\"bronze\",\"gold\",\"silver\",\"bronze\",\"gold\",\"silver\",\"bronze\",\"gold\",\"silver\",\"bronze\",\"gold\",\"silver\",\"bronze\",\"gold\",\"silver\",\"bronze\",\"gold\",\"silver\",\"bronze\",\"gold\",\"silver\",\"bronze\",\"gold\",\"silver\",\"bronze\",\"gold\",\"silver\",\"bronze\",\"gold\",\"silver\",\"bronze\",\"gold\",\"silver\",\"bronze\",\"gold\",\"silver\",\"bronze\",\"bronze\"],\"MedalFill\":[\"#efcf6d\",\"#cccccc\",\"#c59e8a\",\"#efcf6d\",\"#cccccc\",\"#c59e8a\",\"#efcf6d\",\"#cccccc\",\"#c59e8a\",\"#efcf6d\",\"#cccccc\",\"#c59e8a\",\"#efcf6d\",\"#cccccc\",\"#c59e8a\",\"#efcf6d\",\"#cccccc\",\"#c59e8a\",\"#efcf6d\",\"#cccccc\",\"#c59e8a\",\"#efcf6d\",\"#cccccc\",\"#c59e8a\",\"#efcf6d\",\"#cccccc\",\"#c59e8a\",\"#efcf6d\",\"#cccccc\",\"#c59e8a\",\"#efcf6d\",\"#cccccc\",\"#c59e8a\",\"#efcf6d\",\"#cccccc\",\"#c59e8a\",\"#efcf6d\",\"#cccccc\",\"#c59e8a\",\"#efcf6d\",\"#cccccc\",\"#c59e8a\",\"#efcf6d\",\"#cccccc\",\"#c59e8a\",\"#efcf6d\",\"#cccccc\",\"#c59e8a\",\"#efcf6d\",\"#cccccc\",\"#c59e8a\",\"#efcf6d\",\"#cccccc\",\"#c59e8a\",\"#efcf6d\",\"#cccccc\",\"#c59e8a\",\"#efcf6d\",\"#cccccc\",\"#c59e8a\",\"#efcf6d\",\"#cccccc\",\"#c59e8a\",\"#efcf6d\",\"#cccccc\",\"#c59e8a\",\"#efcf6d\",\"#cccccc\",\"#c59e8a\",\"#efcf6d\",\"#cccccc\",\"#c59e8a\",\"#efcf6d\",\"#cccccc\",\"#c59e8a\",\"#efcf6d\",\"#cccccc\",\"#c59e8a\",\"#efcf6d\",\"#cccccc\",\"#c59e8a\",\"#efcf6d\",\"#cccccc\",\"#c59e8a\",\"#c59e8a\"],\"MedalLine\":[\"#c8a850\",\"#b0b0b1\",\"#98715d\",\"#c8a850\",\"#b0b0b1\",\"#98715d\",\"#c8a850\",\"#b0b0b1\",\"#98715d\",\"#c8a850\",\"#b0b0b1\",\"#98715d\",\"#c8a850\",\"#b0b0b1\",\"#98715d\",\"#c8a850\",\"#b0b0b1\",\"#98715d\",\"#c8a850\",\"#b0b0b1\",\"#98715d\",\"#c8a850\",\"#b0b0b1\",\"#98715d\",\"#c8a850\",\"#b0b0b1\",\"#98715d\",\"#c8a850\",\"#b0b0b1\",\"#98715d\",\"#c8a850\",\"#b0b0b1\",\"#98715d\",\"#c8a850\",\"#b0b0b1\",\"#98715d\",\"#c8a850\",\"#b0b0b1\",\"#98715d\",\"#c8a850\",\"#b0b0b1\",\"#98715d\",\"#c8a850\",\"#b0b0b1\",\"#98715d\",\"#c8a850\",\"#b0b0b1\",\"#98715d\",\"#c8a850\",\"#b0b0b1\",\"#98715d\",\"#c8a850\",\"#b0b0b1\",\"#98715d\",\"#c8a850\",\"#b0b0b1\",\"#98715d\",\"#c8a850\",\"#b0b0b1\",\"#98715d\",\"#c8a850\",\"#b0b0b1\",\"#98715d\",\"#c8a850\",\"#b0b0b1\",\"#98715d\",\"#c8a850\",\"#b0b0b1\",\"#98715d\",\"#c8a850\",\"#b0b0b1\",\"#98715d\",\"#c8a850\",\"#b0b0b1\",\"#98715d\",\"#c8a850\",\"#b0b0b1\",\"#98715d\",\"#c8a850\",\"#b0b0b1\",\"#98715d\",\"#c8a850\",\"#b0b0b1\",\"#98715d\",\"#98715d\"],\"MetersBack\":{\"__ndarray__\":\"AAAAAAAAAAD2OrETO7HzPzSH3n8tJvo/+LRJIXLQ4z+4oVs2BggFQE37LXJ6mgZApWIklDbeAUD2G7ItSKkCQBFzDHDwcwNAEXMMcPBzA0A7zUkgLtQMQMAp1V2sVRBA/rQrUbsSAUC4oVs2BggFQMdFF1100QVAuPaQy4qBCkC79onqQSMPQMAp1V2sVRBAV4wxxhhjB0A2Q3IHJ0gLQDvNSSAu1AxAO81JIC7UDEDqKu9YevsVQHzx4sWLFxdALDgfg/MxGEAsOB+D8zEYQO/PDHRHQh1AQ4IAOfAYEUCybdu2bdsRQGZfWb1LHhRAZl9ZvUseFED0/////9MXQEJPwDgDGxtAX4SfXIi6CUDAKdVdrFUQQFLnrrBHehFAiJmZmZmZDUBQWlpaWloWQFBaWlpaWhZAUFpaWlpaFkBQWlpaWloWQCQ5L4v4BBpAHkmSJEmSIEAeSZIkSZIgQN+t7HNITSJA1Imd2ImdHUDUiZ3YiZ0dQNSJndiJnR1AJDkvi/gEGkDUiZ3YiZ0dQNSJndiJnR1AJDkvi/gEGkDUiZ3YiZ0dQB5JkiRJkiBAJDkvi/gEGkAkOS+L+AQaQNSJndiJnR1AqKqqqqqqJUDUtJ8sgU0nQNS0nyyBTSdA363sc0hNIkCoqqqqqqolQNS0nyyBTSdAqKqqqqqqJUDUtJ8sgU0nQNS0nyyBTSdAqKqqqqqqJUDUtJ8sgU0nQNS0nyyBTSdAqKqqqqqqJUDnoosuuugoQOeiiy666ChAiSRJkiQJLECa6xjFuY4tQJrrGMW5ji1A56KLLrroKECJJEmSJAksQIkkSZIkCSxA56KLLrroKEDBZ91ginwqQIkkSZIkCSxA/f////+/M0DYvE9xyRA1QCNJkiRJkjdAI0mSJEmSN0A=\",\"dtype\":\"float64\",\"shape\":[85]},\"Name\":[\"Usain Bolt\",\"Yohan Blake\",\"Justin Gatlin\",\"Usain Bolt\",\"Richard Thompson\",\"Walter Dix\",\"Justin Gatlin\",\"Francis Obikwelu\",\"Maurice Greene\",\"Maurice Greene\",\"Ato Boldon\",\"Obadele Thompson\",\"Donovan Bailey\",\"Frankie Fredericks\",\"Ato Boldon\",\"Linford Christie\",\"Frankie Fredericks\",\"Dennis Mitchell\",\"Carl Lewis\",\"Linford Christie\",\"Calvin Smith\",\"Carl Lewis\",\"Sam Graddy\",\"Ben Johnson\",\"Allan Wells\",\"Silvio Leonard Tartabull\",\"Petar Petrov\",\"Hasely Crawford\",\"Donald Quarrie\",\"Valery Borzov\",\"Valery Borzov\",\"Robert Taylor\",\"Lennox Miller\",\"Jim Hines\",\"Lennox Miller\",\"Charles Greene\",\"Bob Hayes\",\"Enrique Figuerola Camue\",\"Harry Jerome\",\"Armin Hary\",\"David Sime\",\"Peter Radford\",\"Bobby Joe Morrow\",\"W. Thane Baker\",\"Hector Hogan\",\"Lindy Remigino\",\"Herbert McKenley\",\"E. McDonald Bailey\",\"W. Harrison Dillard\",\"H. Norwood \\\"Barney\\\" Ewell\",\"Lloyd LaBeach\",\"Jesse Owens\",\"Ralph Metcalfe\",\"Martinus Osendarp\",\"T. \\\"Eddie\\\" Tolan\",\"Ralph Metcalfe\",\"Arthur Jonath\",\"Percy Williams\",\"John \\\"Jack\\\" London\",\"Georg Lammers\",\"Harold Abrahams\",\"Jackson Scholz\",\"Arthur Porritt\",\"Charles Paddock\",\"Morris Kirksey\",\"Harry Edward\",\"Ralph Craig\",\"Alvah Meyer\",\"Donald Lippincott\",\"Reginald Walker\",\"James Rector\",\"Robert Kerr\",\"Charles \\\"Archie\\\" Hahn\",\"Fay Moulton\",\"Nigel Barker\",\"Charles \\\"Archie\\\" Hahn\",\"Nathaniel Cartmell\",\"Bill Hogenson\",\"Frank Jarvis\",\"J. Walter Tewksbury\",\"Stanley Rowley\",\"Thomas Burke\",\"Fritz Hofmann\",\"Alojz Sokol\",\"Francis Lane\"],\"SelectedName\":[\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"Carl Lewis\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"Jim Hines\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"Jesse Owens\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"Thomas Burke\",\"\",\"\",\"\"],\"Speed\":{\"__ndarray__\":\"D9dd9bfEJEA1SIM0SIMkQNPZ5YvTbSRAEB0COMyjJEBnpfVH8jgkQGugzZx/LiRAUs36M/hNJEDfP/mgskgkQPb3DMpvQyRA9vcMym9DJEBSQDMIIAUkQP8yCgKa6yNAUwg0hUBTJEBnpfVH8jgkQN7OkJi3MyRA9dmM748UJEB3uNvKx/UjQP8yCgKa6yNAKaWUUkopJEBoIvL9Zw8kQFJAMwggBSRAUkAzCCAFJEDuitWqiKAjQBw5cuTIkSNAg/MxOB+DI0CD8zE4H4MjQLW+ZrHQPyNAA1ahlXbhI0B2Xdd1XdcjQKh2L0ZPuSNAqHYvRk+5I0AAAAAAAIgjQBMU9wNwXCNAhZ9ciLoZJED/MgoCmusjQFfYI71o3CNAAAAAAAAAJECcm5ubm5sjQJybm5ubmyNAnJubm5ubI0Ccm5ubm5sjQAxNd7DfaiNAwzAMwzAMI0DDMAzDMAwjQO1zSE0w3iJAsRM7sRM7I0CxEzuxEzsjQLETO7ETOyNADE13sN9qI0CxEzuxEzsjQLETO7ETOyNADE13sN9qI0CxEzuxEzsjQMMwDMMwDCNADE13sN9qI0AMTXew32ojQLETO7ETOyNATGgvob2EIkCUJbBpP1kiQJQlsGk/WSJA7XNITTDeIkBMaC+hvYQiQJQlsGk/WSJATGgvob2EIkCUJbBpP1kiQJQlsGk/WSJATGgvob2EIkCUJbBpP1kiQJQlsGk/WSJATGgvob2EIkAvuuiiiy4iQC+66KKLLiJAt23btm3bIUCTLxs0+bIhQJMvGzT5siFAL7rooosuIkC3bdu2bdshQLdt27Zt2yFAL7rooosuIkBKIC7UnAQiQLdt27Zt2yFAq6qqqqqqIEAebd6nuGQgQPD7vu/7vh9A8Pu+7/u+H0A=\",\"dtype\":\"float64\",\"shape\":[85]},\"Time\":{\"__ndarray__\":\"w/UoXI9CI0AAAAAAAIAjQBSuR+F6lCNA4XoUrkdhI0BI4XoUrscjQFK4HoXr0SNAMzMzMzOzI0C4HoXrUbgjQD0K16NwvSNAPQrXo3C9I0B7FK5H4fojQBSuR+F6FCRArkfhehSuI0BI4XoUrscjQM3MzMzMzCNA7FG4HoXrI0AK16NwPQokQBSuR+F6FCRA16NwPQrXI0BxPQrXo/AjQHsUrkfh+iNAexSuR+H6I0DhehSuR2EkQHE9CtejcCRAAAAAAACAJEAAAAAAAIAkQEjhehSuxyRAH4XrUbgeJEApXI/C9SgkQEjhehSuRyRASOF6FK5HJEB7FK5H4XokQClcj8L1qCRAZmZmZmbmI0AUrkfhehQkQKRwPQrXIyRAAAAAAAAAJEBmZmZmZmYkQGZmZmZmZiRAZmZmZmZmJEBmZmZmZmYkQJqZmZmZmSRAAAAAAAAAJUAAAAAAAAAlQDMzMzMzMyVAzczMzMzMJEDNzMzMzMwkQM3MzMzMzCRAmpmZmZmZJEDNzMzMzMwkQM3MzMzMzCRAmpmZmZmZJEDNzMzMzMwkQAAAAAAAACVAmpmZmZmZJECamZmZmZkkQM3MzMzMzCRAmpmZmZmZJUDNzMzMzMwlQM3MzMzMzCVAMzMzMzMzJUCamZmZmZklQM3MzMzMzCVAmpmZmZmZJUDNzMzMzMwlQM3MzMzMzCVAmpmZmZmZJUDNzMzMzMwlQM3MzMzMzCVAmpmZmZmZJUAAAAAAAAAmQAAAAAAAACZAZmZmZmZmJkCamZmZmZkmQJqZmZmZmSZAAAAAAAAAJkBmZmZmZmYmQGZmZmZmZiZAAAAAAAAAJkAzMzMzMzMmQGZmZmZmZiZAAAAAAAAAKEBmZmZmZmYoQDMzMzMzMylAMzMzMzMzKUA=\",\"dtype\":\"float64\",\"shape\":[85]},\"Year\":[2012,2012,2012,2008,2008,2008,2004,2004,2004,2000,2000,2000,1996,1996,1996,1992,1992,1992,1988,1988,1988,1984,1984,1984,1980,1980,1980,1976,1976,1976,1972,1972,1972,1968,1968,1968,1964,1964,1964,1960,1960,1960,1956,1956,1956,1952,1952,1952,1948,1948,1948,1936,1936,1936,1932,1932,1932,1928,1928,1928,1924,1924,1924,1920,1920,1920,1912,1912,1912,1908,1908,1908,1906,1906,1906,1904,1904,1904,1900,1900,1900,1896,1896,1896,1896],\"index\":[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84]},\"selected\":{\"id\":\"1025\"},\"selection_policy\":{\"id\":\"1026\"}},\"id\":\"1002\",\"type\":\"ColumnDataSource\"},{\"attributes\":{\"fill_alpha\":{\"value\":0.5},\"fill_color\":{\"field\":\"MedalFill\"},\"line_color\":{\"field\":\"MedalLine\"},\"size\":{\"units\":\"screen\",\"value\":10},\"x\":{\"field\":\"MetersBack\"},\"y\":{\"field\":\"Year\"}},\"id\":\"1003\",\"type\":\"Circle\"},{\"attributes\":{},\"id\":\"1025\",\"type\":\"Selection\"},{\"attributes\":{},\"id\":\"1026\",\"type\":\"UnionRenderers\"},{\"attributes\":{\"source\":{\"id\":\"1002\"}},\"id\":\"1094\",\"type\":\"CDSView\"},{\"attributes\":{\"data_source\":{\"id\":\"1002\"},\"glyph\":{\"id\":\"1003\"},\"hover_glyph\":null,\"muted_glyph\":null,\"view\":{\"id\":\"1092\"}},\"id\":\"1091\",\"type\":\"GlyphRenderer\"},{\"attributes\":{\"text\":{\"value\":\"No Olympics in 1940 or 1944\"},\"text_align\":\"center\",\"text_baseline\":\"middle\",\"text_color\":{\"value\":\"silver\"},\"text_font_size\":{\"value\":\"9pt\"},\"text_font_style\":\"italic\",\"x\":{\"value\":7.5},\"y\":{\"value\":1942}},\"id\":\"1005\",\"type\":\"Text\"},{\"attributes\":{},\"id\":\"1123\",\"type\":\"Selection\"},{\"attributes\":{\"data_source\":{\"id\":\"1002\"},\"glyph\":{\"id\":\"1004\"},\"hover_glyph\":null,\"muted_glyph\":null,\"view\":{\"id\":\"1094\"}},\"id\":\"1093\",\"type\":\"GlyphRenderer\"},{\"attributes\":{\"source\":{\"id\":\"1002\"}},\"id\":\"1092\",\"type\":\"CDSView\"},{\"attributes\":{\"interval\":12,\"num_minor_ticks\":0},\"id\":\"1104\",\"type\":\"SingleIntervalTicker\"},{\"attributes\":{\"text\":{\"field\":\"SelectedName\"},\"text_baseline\":\"middle\",\"text_font_size\":{\"value\":\"9pt\"},\"x\":{\"field\":\"MetersBack\"},\"x_offset\":{\"value\":10},\"y\":{\"field\":\"Year\"}},\"id\":\"1004\",\"type\":\"Text\"},{\"attributes\":{\"data\":{},\"selected\":{\"id\":\"1123\"},\"selection_policy\":{\"id\":\"1124\"}},\"id\":\"1095\",\"type\":\"ColumnDataSource\"},{\"attributes\":{\"source\":{\"id\":\"1095\"}},\"id\":\"1097\",\"type\":\"CDSView\"},{\"attributes\":{\"active_drag\":\"auto\",\"active_inspect\":\"auto\",\"active_multi\":null,\"active_scroll\":\"auto\",\"active_tap\":\"auto\"},\"id\":\"1117\",\"type\":\"Toolbar\"},{\"attributes\":{\"formatter\":{\"id\":\"1119\"},\"major_tick_in\":-5,\"major_tick_out\":10,\"ticker\":{\"id\":\"1104\"}},\"id\":\"1105\",\"type\":\"LinearAxis\"},{\"attributes\":{\"range_padding\":0.05},\"id\":\"1086\",\"type\":\"DataRange1d\"},{\"attributes\":{\"interval\":5,\"num_minor_ticks\":0},\"id\":\"1100\",\"type\":\"SingleIntervalTicker\"},{\"attributes\":{},\"id\":\"1124\",\"type\":\"UnionRenderers\"},{\"attributes\":{},\"id\":\"1115\",\"type\":\"LinearScale\"},{\"attributes\":{\"text\":\"Usain Bolt vs. 116 years of Olympic sprinters\"},\"id\":\"1090\",\"type\":\"Title\"},{\"attributes\":{},\"id\":\"1120\",\"type\":\"BasicTickFormatter\"},{\"attributes\":{\"end\":0,\"start\":25.571428571428566},\"id\":\"1085\",\"type\":\"Range1d\"},{\"attributes\":{\"axis\":null,\"grid_line_dash\":[6],\"ticker\":{\"id\":\"1100\"}},\"id\":\"1103\",\"type\":\"Grid\"},{\"attributes\":{\"axis_label\":\"Meters behind 2012 Bolt\",\"axis_label_text_font_style\":\"bold\",\"axis_line_color\":null,\"formatter\":{\"id\":\"1120\"},\"major_tick_line_color\":null,\"ticker\":{\"id\":\"1100\"}},\"id\":\"1101\",\"type\":\"LinearAxis\"},{\"attributes\":{},\"id\":\"1118\",\"type\":\"LinearScale\"}],\"root_ids\":[\"1087\"]},\"title\":\"Bokeh Application\",\"version\":\"2.0.1\"}};\n",
       "  var render_items = [{\"docid\":\"d1eab26d-e515-4c1e-a032-d6fc023cc93e\",\"root_ids\":[\"1087\"],\"roots\":{\"1087\":\"c886dffc-9e9c-4271-b1ad-e4cfdd9f03c6\"}}];\n",
       "  root.Bokeh.embed.embed_items_notebook(docs_json, render_items);\n",
       "\n",
       "  }\n",
       "  if (root.Bokeh !== undefined) {\n",
       "    embed_document(root);\n",
       "  } else {\n",
       "    var attempts = 0;\n",
       "    var timer = setInterval(function(root) {\n",
       "      if (root.Bokeh !== undefined) {\n",
       "        clearInterval(timer);\n",
       "        embed_document(root);\n",
       "      } else {\n",
       "        attempts++;\n",
       "        if (attempts > 100) {\n",
       "          clearInterval(timer);\n",
       "          console.log(\"Bokeh: ERROR: Unable to run BokehJS code because BokehJS library is missing\");\n",
       "        }\n",
       "      }\n",
       "    }, 10, root)\n",
       "  }\n",
       "})(window);"
      ],
      "application/vnd.bokehjs_exec.v0+json": ""
     },
     "metadata": {
      "application/vnd.bokehjs_exec.v0+json": {
       "id": "1087"
      }
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "show(plot)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Adding a Hover Tool\n",
    "\n",
    "Finally we add a hover tool to display those extra columns that we put into our column data source. We use the template syntax for the tooltips, to have more control over the appearance. Tools can be added using the `plot.add_tools` method."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [],
   "source": [
    "from bokeh.models import HoverTool"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [],
   "source": [
    "tooltips = \"\"\"\n",
    "<div>\n",
    "    <span style=\"font-size: 15px;\">@Name</span>&nbsp;\n",
    "    <span style=\"font-size: 10px; color: #666;\">(@Abbrev)</span>\n",
    "</div>\n",
    "<div>\n",
    "    <span style=\"font-size: 17px; font-weight: bold;\">@Time{0.00}</span>&nbsp;\n",
    "    <span style=\"font-size: 10px; color: #666;\">@Year</span>\n",
    "</div>\n",
    "<div style=\"font-size: 11px; color: #666;\">@{MetersBack}{0.00} meters behind</div>\n",
    "\"\"\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [],
   "source": [
    "xdr = Range1d(start=sprint.MetersBack.max()+2, end=0)  # +2 is for padding\n",
    "ydr = DataRange1d(range_padding=0.05)  \n",
    "\n",
    "plot = Plot(x_range=xdr, y_range=ydr, **plot_options)\n",
    "plot.title.text = \"Usain Bolt vs. 116 years of Olympic sprinters\"\n",
    "medal = plot.add_glyph(source, medal_glyph)  # we need this renderer to configure the hover tool\n",
    "plot.add_glyph(source, athlete_glyph)\n",
    "plot.add_glyph(no_olympics_glyph)\n",
    "\n",
    "xticker = SingleIntervalTicker(interval=5, num_minor_ticks=0)\n",
    "xaxis = LinearAxis(ticker=xticker, axis_line_color=None, major_tick_line_color=None,\n",
    "                   axis_label=\"Meters behind 2012 Bolt\", axis_label_text_font_size=\"10pt\", \n",
    "                   axis_label_text_font_style=\"bold\")\n",
    "plot.add_layout(xaxis, \"below\")\n",
    "\n",
    "xgrid = Grid(dimension=0, ticker=xaxis.ticker, grid_line_dash=\"dashed\")\n",
    "plot.add_layout(xgrid)\n",
    "\n",
    "yticker = SingleIntervalTicker(interval=12, num_minor_ticks=0)\n",
    "yaxis = LinearAxis(ticker=yticker, major_tick_in=-5, major_tick_out=10)\n",
    "plot.add_layout(yaxis, \"right\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [],
   "source": [
    "hover = HoverTool(tooltips=tooltips, renderers=[medal])\n",
    "plot.add_tools(hover)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "\n",
       "\n",
       "\n",
       "\n",
       "\n",
       "\n",
       "  <div class=\"bk-root\" id=\"779fcf82-0172-4c88-969c-f5d6fc98c7df\" data-root-id=\"1195\"></div>\n"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "application/javascript": [
       "(function(root) {\n",
       "  function embed_document(root) {\n",
       "    \n",
       "  var docs_json = {\"4d356d91-325a-4a80-9f74-0b802205581f\":{\"roots\":{\"references\":[{\"attributes\":{\"below\":[{\"id\":\"1207\"}],\"center\":[{\"id\":\"1209\"}],\"outline_line_color\":null,\"plot_height\":480,\"plot_width\":800,\"renderers\":[{\"id\":\"1199\"},{\"id\":\"1201\"},{\"id\":\"1204\"}],\"right\":[{\"id\":\"1211\"}],\"title\":{\"id\":\"1198\"},\"toolbar\":{\"id\":\"1214\"},\"toolbar_location\":null,\"x_range\":{\"id\":\"1193\"},\"x_scale\":{\"id\":\"1236\"},\"y_range\":{\"id\":\"1194\"},\"y_scale\":{\"id\":\"1233\"}},\"id\":\"1195\",\"type\":\"Plot\"},{\"attributes\":{\"data_source\":{\"id\":\"1002\"},\"glyph\":{\"id\":\"1004\"},\"hover_glyph\":null,\"muted_glyph\":null,\"view\":{\"id\":\"1202\"}},\"id\":\"1201\",\"type\":\"GlyphRenderer\"},{\"attributes\":{},\"id\":\"1236\",\"type\":\"LinearScale\"},{\"attributes\":{\"source\":{\"id\":\"1002\"}},\"id\":\"1200\",\"type\":\"CDSView\"},{\"attributes\":{\"active_drag\":\"auto\",\"active_inspect\":\"auto\",\"active_multi\":null,\"active_scroll\":\"auto\",\"active_tap\":\"auto\",\"tools\":[{\"id\":\"1213\"}]},\"id\":\"1214\",\"type\":\"Toolbar\"},{\"attributes\":{\"data\":{},\"selected\":{\"id\":\"1241\"},\"selection_policy\":{\"id\":\"1242\"}},\"id\":\"1203\",\"type\":\"ColumnDataSource\"},{\"attributes\":{\"data_source\":{\"id\":\"1203\"},\"glyph\":{\"id\":\"1005\"},\"hover_glyph\":null,\"muted_glyph\":null,\"view\":{\"id\":\"1205\"}},\"id\":\"1204\",\"type\":\"GlyphRenderer\"},{\"attributes\":{\"source\":{\"id\":\"1203\"}},\"id\":\"1205\",\"type\":\"CDSView\"},{\"attributes\":{\"formatter\":{\"id\":\"1237\"},\"major_tick_in\":-5,\"major_tick_out\":10,\"ticker\":{\"id\":\"1210\"}},\"id\":\"1211\",\"type\":\"LinearAxis\"},{\"attributes\":{\"axis_label\":\"Meters behind 2012 Bolt\",\"axis_label_text_font_style\":\"bold\",\"axis_line_color\":null,\"formatter\":{\"id\":\"1238\"},\"major_tick_line_color\":null,\"ticker\":{\"id\":\"1206\"}},\"id\":\"1207\",\"type\":\"LinearAxis\"},{\"attributes\":{\"data\":{\"Abbrev\":[\"JAM\",\"JAM\",\"USA\",\"JAM\",\"TRI\",\"USA\",\"USA\",\"POR\",\"USA\",\"USA\",\"TRI\",\"BAR\",\"CAN\",\"NAM\",\"TRI\",\"GBR\",\"NAM\",\"USA\",\"USA\",\"GBR\",\"USA\",\"USA\",\"USA\",\"CAN\",\"GBR\",\"CUB\",\"BUL\",\"TRI\",\"JAM\",\"URS\",\"URS\",\"USA\",\"JAM\",\"USA\",\"JAM\",\"USA\",\"USA\",\"CUB\",\"CAN\",\"EUA\",\"USA\",\"GBR\",\"USA\",\"USA\",\"AUS\",\"USA\",\"JAM\",\"GBR\",\"USA\",\"USA\",\"PAN\",\"USA\",\"USA\",\"NED\",\"USA\",\"USA\",\"GER\",\"CAN\",\"GBR\",\"GER\",\"GBR\",\"USA\",\"NZL\",\"USA\",\"USA\",\"GBR\",\"USA\",\"USA\",\"USA\",\"RSA\",\"USA\",\"CAN\",\"USA\",\"USA\",\"AUS\",\"USA\",\"USA\",\"USA\",\"USA\",\"USA\",\"AUS\",\"USA\",\"GER\",\"HUN\",\"USA\"],\"Country\":[\"Jamaica\",\"Jamaica\",\"United States\",\"Jamaica\",\"Trinidad and Tobago\",\"United States\",\"United States\",\"Portugal\",\"United States\",\"United States\",\"Trinidad and Tobago\",\"Barbados\",\"Canada\",\"Namibia\",\"Trinidad and Tobago\",\"Britain\",\"Namibia\",\"United States\",\"United States\",\"Britain\",\"United States\",\"United States\",\"United States\",\"Canada\",\"Britain\",\"Cuba\",\"Bulgaria\",\"Trinidad and Tobago\",\"Jamaica\",\"Soviet Union\",\"Soviet Union\",\"United States\",\"Jamaica\",\"United States\",\"Jamaica\",\"United States\",\"United States\",\"Cuba\",\"Canada\",\"United Team of Germany\",\"United States\",\"Britain\",\"United States\",\"United States\",\"Australia\",\"United States\",\"Jamaica\",\"Britain\",\"United States\",\"United States\",\"Panama\",\"United States\",\"United States\",\"Netherlands\",\"United States\",\"United States\",\"Germany\",\"Canada\",\"Britain\",\"Germany\",\"Britain\",\"United States\",\"New Zealand\",\"United States\",\"United States\",\"Britain\",\"United States\",\"United States\",\"United States\",\"South Africa\",\"United States\",\"Canada\",\"United States\",\"United States\",\"Australia\",\"United States\",\"United States\",\"United States\",\"United States\",\"United States\",\"Australia\",\"United States\",\"Germany\",\"Hungary\",\"United States\"],\"Medal\":[\"gold\",\"silver\",\"bronze\",\"gold\",\"silver\",\"bronze\",\"gold\",\"silver\",\"bronze\",\"gold\",\"silver\",\"bronze\",\"gold\",\"silver\",\"bronze\",\"gold\",\"silver\",\"bronze\",\"gold\",\"silver\",\"bronze\",\"gold\",\"silver\",\"bronze\",\"gold\",\"silver\",\"bronze\",\"gold\",\"silver\",\"bronze\",\"gold\",\"silver\",\"bronze\",\"gold\",\"silver\",\"bronze\",\"gold\",\"silver\",\"bronze\",\"gold\",\"silver\",\"bronze\",\"gold\",\"silver\",\"bronze\",\"gold\",\"silver\",\"bronze\",\"gold\",\"silver\",\"bronze\",\"gold\",\"silver\",\"bronze\",\"gold\",\"silver\",\"bronze\",\"gold\",\"silver\",\"bronze\",\"gold\",\"silver\",\"bronze\",\"gold\",\"silver\",\"bronze\",\"gold\",\"silver\",\"bronze\",\"gold\",\"silver\",\"bronze\",\"gold\",\"silver\",\"bronze\",\"gold\",\"silver\",\"bronze\",\"gold\",\"silver\",\"bronze\",\"gold\",\"silver\",\"bronze\",\"bronze\"],\"MedalFill\":[\"#efcf6d\",\"#cccccc\",\"#c59e8a\",\"#efcf6d\",\"#cccccc\",\"#c59e8a\",\"#efcf6d\",\"#cccccc\",\"#c59e8a\",\"#efcf6d\",\"#cccccc\",\"#c59e8a\",\"#efcf6d\",\"#cccccc\",\"#c59e8a\",\"#efcf6d\",\"#cccccc\",\"#c59e8a\",\"#efcf6d\",\"#cccccc\",\"#c59e8a\",\"#efcf6d\",\"#cccccc\",\"#c59e8a\",\"#efcf6d\",\"#cccccc\",\"#c59e8a\",\"#efcf6d\",\"#cccccc\",\"#c59e8a\",\"#efcf6d\",\"#cccccc\",\"#c59e8a\",\"#efcf6d\",\"#cccccc\",\"#c59e8a\",\"#efcf6d\",\"#cccccc\",\"#c59e8a\",\"#efcf6d\",\"#cccccc\",\"#c59e8a\",\"#efcf6d\",\"#cccccc\",\"#c59e8a\",\"#efcf6d\",\"#cccccc\",\"#c59e8a\",\"#efcf6d\",\"#cccccc\",\"#c59e8a\",\"#efcf6d\",\"#cccccc\",\"#c59e8a\",\"#efcf6d\",\"#cccccc\",\"#c59e8a\",\"#efcf6d\",\"#cccccc\",\"#c59e8a\",\"#efcf6d\",\"#cccccc\",\"#c59e8a\",\"#efcf6d\",\"#cccccc\",\"#c59e8a\",\"#efcf6d\",\"#cccccc\",\"#c59e8a\",\"#efcf6d\",\"#cccccc\",\"#c59e8a\",\"#efcf6d\",\"#cccccc\",\"#c59e8a\",\"#efcf6d\",\"#cccccc\",\"#c59e8a\",\"#efcf6d\",\"#cccccc\",\"#c59e8a\",\"#efcf6d\",\"#cccccc\",\"#c59e8a\",\"#c59e8a\"],\"MedalLine\":[\"#c8a850\",\"#b0b0b1\",\"#98715d\",\"#c8a850\",\"#b0b0b1\",\"#98715d\",\"#c8a850\",\"#b0b0b1\",\"#98715d\",\"#c8a850\",\"#b0b0b1\",\"#98715d\",\"#c8a850\",\"#b0b0b1\",\"#98715d\",\"#c8a850\",\"#b0b0b1\",\"#98715d\",\"#c8a850\",\"#b0b0b1\",\"#98715d\",\"#c8a850\",\"#b0b0b1\",\"#98715d\",\"#c8a850\",\"#b0b0b1\",\"#98715d\",\"#c8a850\",\"#b0b0b1\",\"#98715d\",\"#c8a850\",\"#b0b0b1\",\"#98715d\",\"#c8a850\",\"#b0b0b1\",\"#98715d\",\"#c8a850\",\"#b0b0b1\",\"#98715d\",\"#c8a850\",\"#b0b0b1\",\"#98715d\",\"#c8a850\",\"#b0b0b1\",\"#98715d\",\"#c8a850\",\"#b0b0b1\",\"#98715d\",\"#c8a850\",\"#b0b0b1\",\"#98715d\",\"#c8a850\",\"#b0b0b1\",\"#98715d\",\"#c8a850\",\"#b0b0b1\",\"#98715d\",\"#c8a850\",\"#b0b0b1\",\"#98715d\",\"#c8a850\",\"#b0b0b1\",\"#98715d\",\"#c8a850\",\"#b0b0b1\",\"#98715d\",\"#c8a850\",\"#b0b0b1\",\"#98715d\",\"#c8a850\",\"#b0b0b1\",\"#98715d\",\"#c8a850\",\"#b0b0b1\",\"#98715d\",\"#c8a850\",\"#b0b0b1\",\"#98715d\",\"#c8a850\",\"#b0b0b1\",\"#98715d\",\"#c8a850\",\"#b0b0b1\",\"#98715d\",\"#98715d\"],\"MetersBack\":{\"__ndarray__\":\"AAAAAAAAAAD2OrETO7HzPzSH3n8tJvo/+LRJIXLQ4z+4oVs2BggFQE37LXJ6mgZApWIklDbeAUD2G7ItSKkCQBFzDHDwcwNAEXMMcPBzA0A7zUkgLtQMQMAp1V2sVRBA/rQrUbsSAUC4oVs2BggFQMdFF1100QVAuPaQy4qBCkC79onqQSMPQMAp1V2sVRBAV4wxxhhjB0A2Q3IHJ0gLQDvNSSAu1AxAO81JIC7UDEDqKu9YevsVQHzx4sWLFxdALDgfg/MxGEAsOB+D8zEYQO/PDHRHQh1AQ4IAOfAYEUCybdu2bdsRQGZfWb1LHhRAZl9ZvUseFED0/////9MXQEJPwDgDGxtAX4SfXIi6CUDAKdVdrFUQQFLnrrBHehFAiJmZmZmZDUBQWlpaWloWQFBaWlpaWhZAUFpaWlpaFkBQWlpaWloWQCQ5L4v4BBpAHkmSJEmSIEAeSZIkSZIgQN+t7HNITSJA1Imd2ImdHUDUiZ3YiZ0dQNSJndiJnR1AJDkvi/gEGkDUiZ3YiZ0dQNSJndiJnR1AJDkvi/gEGkDUiZ3YiZ0dQB5JkiRJkiBAJDkvi/gEGkAkOS+L+AQaQNSJndiJnR1AqKqqqqqqJUDUtJ8sgU0nQNS0nyyBTSdA363sc0hNIkCoqqqqqqolQNS0nyyBTSdAqKqqqqqqJUDUtJ8sgU0nQNS0nyyBTSdAqKqqqqqqJUDUtJ8sgU0nQNS0nyyBTSdAqKqqqqqqJUDnoosuuugoQOeiiy666ChAiSRJkiQJLECa6xjFuY4tQJrrGMW5ji1A56KLLrroKECJJEmSJAksQIkkSZIkCSxA56KLLrroKEDBZ91ginwqQIkkSZIkCSxA/f////+/M0DYvE9xyRA1QCNJkiRJkjdAI0mSJEmSN0A=\",\"dtype\":\"float64\",\"shape\":[85]},\"Name\":[\"Usain Bolt\",\"Yohan Blake\",\"Justin Gatlin\",\"Usain Bolt\",\"Richard Thompson\",\"Walter Dix\",\"Justin Gatlin\",\"Francis Obikwelu\",\"Maurice Greene\",\"Maurice Greene\",\"Ato Boldon\",\"Obadele Thompson\",\"Donovan Bailey\",\"Frankie Fredericks\",\"Ato Boldon\",\"Linford Christie\",\"Frankie Fredericks\",\"Dennis Mitchell\",\"Carl Lewis\",\"Linford Christie\",\"Calvin Smith\",\"Carl Lewis\",\"Sam Graddy\",\"Ben Johnson\",\"Allan Wells\",\"Silvio Leonard Tartabull\",\"Petar Petrov\",\"Hasely Crawford\",\"Donald Quarrie\",\"Valery Borzov\",\"Valery Borzov\",\"Robert Taylor\",\"Lennox Miller\",\"Jim Hines\",\"Lennox Miller\",\"Charles Greene\",\"Bob Hayes\",\"Enrique Figuerola Camue\",\"Harry Jerome\",\"Armin Hary\",\"David Sime\",\"Peter Radford\",\"Bobby Joe Morrow\",\"W. Thane Baker\",\"Hector Hogan\",\"Lindy Remigino\",\"Herbert McKenley\",\"E. McDonald Bailey\",\"W. Harrison Dillard\",\"H. Norwood \\\"Barney\\\" Ewell\",\"Lloyd LaBeach\",\"Jesse Owens\",\"Ralph Metcalfe\",\"Martinus Osendarp\",\"T. \\\"Eddie\\\" Tolan\",\"Ralph Metcalfe\",\"Arthur Jonath\",\"Percy Williams\",\"John \\\"Jack\\\" London\",\"Georg Lammers\",\"Harold Abrahams\",\"Jackson Scholz\",\"Arthur Porritt\",\"Charles Paddock\",\"Morris Kirksey\",\"Harry Edward\",\"Ralph Craig\",\"Alvah Meyer\",\"Donald Lippincott\",\"Reginald Walker\",\"James Rector\",\"Robert Kerr\",\"Charles \\\"Archie\\\" Hahn\",\"Fay Moulton\",\"Nigel Barker\",\"Charles \\\"Archie\\\" Hahn\",\"Nathaniel Cartmell\",\"Bill Hogenson\",\"Frank Jarvis\",\"J. Walter Tewksbury\",\"Stanley Rowley\",\"Thomas Burke\",\"Fritz Hofmann\",\"Alojz Sokol\",\"Francis Lane\"],\"SelectedName\":[\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"Carl Lewis\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"Jim Hines\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"Jesse Owens\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"Thomas Burke\",\"\",\"\",\"\"],\"Speed\":{\"__ndarray__\":\"D9dd9bfEJEA1SIM0SIMkQNPZ5YvTbSRAEB0COMyjJEBnpfVH8jgkQGugzZx/LiRAUs36M/hNJEDfP/mgskgkQPb3DMpvQyRA9vcMym9DJEBSQDMIIAUkQP8yCgKa6yNAUwg0hUBTJEBnpfVH8jgkQN7OkJi3MyRA9dmM748UJEB3uNvKx/UjQP8yCgKa6yNAKaWUUkopJEBoIvL9Zw8kQFJAMwggBSRAUkAzCCAFJEDuitWqiKAjQBw5cuTIkSNAg/MxOB+DI0CD8zE4H4MjQLW+ZrHQPyNAA1ahlXbhI0B2Xdd1XdcjQKh2L0ZPuSNAqHYvRk+5I0AAAAAAAIgjQBMU9wNwXCNAhZ9ciLoZJED/MgoCmusjQFfYI71o3CNAAAAAAAAAJECcm5ubm5sjQJybm5ubmyNAnJubm5ubI0Ccm5ubm5sjQAxNd7DfaiNAwzAMwzAMI0DDMAzDMAwjQO1zSE0w3iJAsRM7sRM7I0CxEzuxEzsjQLETO7ETOyNADE13sN9qI0CxEzuxEzsjQLETO7ETOyNADE13sN9qI0CxEzuxEzsjQMMwDMMwDCNADE13sN9qI0AMTXew32ojQLETO7ETOyNATGgvob2EIkCUJbBpP1kiQJQlsGk/WSJA7XNITTDeIkBMaC+hvYQiQJQlsGk/WSJATGgvob2EIkCUJbBpP1kiQJQlsGk/WSJATGgvob2EIkCUJbBpP1kiQJQlsGk/WSJATGgvob2EIkAvuuiiiy4iQC+66KKLLiJAt23btm3bIUCTLxs0+bIhQJMvGzT5siFAL7rooosuIkC3bdu2bdshQLdt27Zt2yFAL7rooosuIkBKIC7UnAQiQLdt27Zt2yFAq6qqqqqqIEAebd6nuGQgQPD7vu/7vh9A8Pu+7/u+H0A=\",\"dtype\":\"float64\",\"shape\":[85]},\"Time\":{\"__ndarray__\":\"w/UoXI9CI0AAAAAAAIAjQBSuR+F6lCNA4XoUrkdhI0BI4XoUrscjQFK4HoXr0SNAMzMzMzOzI0C4HoXrUbgjQD0K16NwvSNAPQrXo3C9I0B7FK5H4fojQBSuR+F6FCRArkfhehSuI0BI4XoUrscjQM3MzMzMzCNA7FG4HoXrI0AK16NwPQokQBSuR+F6FCRA16NwPQrXI0BxPQrXo/AjQHsUrkfh+iNAexSuR+H6I0DhehSuR2EkQHE9CtejcCRAAAAAAACAJEAAAAAAAIAkQEjhehSuxyRAH4XrUbgeJEApXI/C9SgkQEjhehSuRyRASOF6FK5HJEB7FK5H4XokQClcj8L1qCRAZmZmZmbmI0AUrkfhehQkQKRwPQrXIyRAAAAAAAAAJEBmZmZmZmYkQGZmZmZmZiRAZmZmZmZmJEBmZmZmZmYkQJqZmZmZmSRAAAAAAAAAJUAAAAAAAAAlQDMzMzMzMyVAzczMzMzMJEDNzMzMzMwkQM3MzMzMzCRAmpmZmZmZJEDNzMzMzMwkQM3MzMzMzCRAmpmZmZmZJEDNzMzMzMwkQAAAAAAAACVAmpmZmZmZJECamZmZmZkkQM3MzMzMzCRAmpmZmZmZJUDNzMzMzMwlQM3MzMzMzCVAMzMzMzMzJUCamZmZmZklQM3MzMzMzCVAmpmZmZmZJUDNzMzMzMwlQM3MzMzMzCVAmpmZmZmZJUDNzMzMzMwlQM3MzMzMzCVAmpmZmZmZJUAAAAAAAAAmQAAAAAAAACZAZmZmZmZmJkCamZmZmZkmQJqZmZmZmSZAAAAAAAAAJkBmZmZmZmYmQGZmZmZmZiZAAAAAAAAAJkAzMzMzMzMmQGZmZmZmZiZAAAAAAAAAKEBmZmZmZmYoQDMzMzMzMylAMzMzMzMzKUA=\",\"dtype\":\"float64\",\"shape\":[85]},\"Year\":[2012,2012,2012,2008,2008,2008,2004,2004,2004,2000,2000,2000,1996,1996,1996,1992,1992,1992,1988,1988,1988,1984,1984,1984,1980,1980,1980,1976,1976,1976,1972,1972,1972,1968,1968,1968,1964,1964,1964,1960,1960,1960,1956,1956,1956,1952,1952,1952,1948,1948,1948,1936,1936,1936,1932,1932,1932,1928,1928,1928,1924,1924,1924,1920,1920,1920,1912,1912,1912,1908,1908,1908,1906,1906,1906,1904,1904,1904,1900,1900,1900,1896,1896,1896,1896],\"index\":[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84]},\"selected\":{\"id\":\"1025\"},\"selection_policy\":{\"id\":\"1026\"}},\"id\":\"1002\",\"type\":\"ColumnDataSource\"},{\"attributes\":{\"axis\":null,\"grid_line_dash\":[6],\"ticker\":{\"id\":\"1206\"}},\"id\":\"1209\",\"type\":\"Grid\"},{\"attributes\":{},\"id\":\"1238\",\"type\":\"BasicTickFormatter\"},{\"attributes\":{\"interval\":12,\"num_minor_ticks\":0},\"id\":\"1210\",\"type\":\"SingleIntervalTicker\"},{\"attributes\":{\"callback\":null,\"renderers\":[{\"id\":\"1199\"}],\"tooltips\":\"\\n<div>\\n    <span style=\\\"font-size: 15px;\\\">@Name</span>&nbsp;\\n    <span style=\\\"font-size: 10px; color: #666;\\\">(@Abbrev)</span>\\n</div>\\n<div>\\n    <span style=\\\"font-size: 17px; font-weight: bold;\\\">@Time{0.00}</span>&nbsp;\\n    <span style=\\\"font-size: 10px; color: #666;\\\">@Year</span>\\n</div>\\n<div style=\\\"font-size: 11px; color: #666;\\\">@{MetersBack}{0.00} meters behind</div>\\n\"},\"id\":\"1213\",\"type\":\"HoverTool\"},{\"attributes\":{\"fill_alpha\":{\"value\":0.5},\"fill_color\":{\"field\":\"MedalFill\"},\"line_color\":{\"field\":\"MedalLine\"},\"size\":{\"units\":\"screen\",\"value\":10},\"x\":{\"field\":\"MetersBack\"},\"y\":{\"field\":\"Year\"}},\"id\":\"1003\",\"type\":\"Circle\"},{\"attributes\":{},\"id\":\"1237\",\"type\":\"BasicTickFormatter\"},{\"attributes\":{},\"id\":\"1025\",\"type\":\"Selection\"},{\"attributes\":{},\"id\":\"1242\",\"type\":\"UnionRenderers\"},{\"attributes\":{},\"id\":\"1026\",\"type\":\"UnionRenderers\"},{\"attributes\":{\"text\":{\"value\":\"No Olympics in 1940 or 1944\"},\"text_align\":\"center\",\"text_baseline\":\"middle\",\"text_color\":{\"value\":\"silver\"},\"text_font_size\":{\"value\":\"9pt\"},\"text_font_style\":\"italic\",\"x\":{\"value\":7.5},\"y\":{\"value\":1942}},\"id\":\"1005\",\"type\":\"Text\"},{\"attributes\":{\"text\":{\"field\":\"SelectedName\"},\"text_baseline\":\"middle\",\"text_font_size\":{\"value\":\"9pt\"},\"x\":{\"field\":\"MetersBack\"},\"x_offset\":{\"value\":10},\"y\":{\"field\":\"Year\"}},\"id\":\"1004\",\"type\":\"Text\"},{\"attributes\":{},\"id\":\"1233\",\"type\":\"LinearScale\"},{\"attributes\":{\"range_padding\":0.05},\"id\":\"1194\",\"type\":\"DataRange1d\"},{\"attributes\":{},\"id\":\"1241\",\"type\":\"Selection\"},{\"attributes\":{\"text\":\"Usain Bolt vs. 116 years of Olympic sprinters\"},\"id\":\"1198\",\"type\":\"Title\"},{\"attributes\":{\"end\":0,\"start\":25.571428571428566},\"id\":\"1193\",\"type\":\"Range1d\"},{\"attributes\":{\"source\":{\"id\":\"1002\"}},\"id\":\"1202\",\"type\":\"CDSView\"},{\"attributes\":{\"interval\":5,\"num_minor_ticks\":0},\"id\":\"1206\",\"type\":\"SingleIntervalTicker\"},{\"attributes\":{\"data_source\":{\"id\":\"1002\"},\"glyph\":{\"id\":\"1003\"},\"hover_glyph\":null,\"muted_glyph\":null,\"view\":{\"id\":\"1200\"}},\"id\":\"1199\",\"type\":\"GlyphRenderer\"}],\"root_ids\":[\"1195\"]},\"title\":\"Bokeh Application\",\"version\":\"2.0.1\"}};\n",
       "  var render_items = [{\"docid\":\"4d356d91-325a-4a80-9f74-0b802205581f\",\"root_ids\":[\"1195\"],\"roots\":{\"1195\":\"779fcf82-0172-4c88-969c-f5d6fc98c7df\"}}];\n",
       "  root.Bokeh.embed.embed_items_notebook(docs_json, render_items);\n",
       "\n",
       "  }\n",
       "  if (root.Bokeh !== undefined) {\n",
       "    embed_document(root);\n",
       "  } else {\n",
       "    var attempts = 0;\n",
       "    var timer = setInterval(function(root) {\n",
       "      if (root.Bokeh !== undefined) {\n",
       "        clearInterval(timer);\n",
       "        embed_document(root);\n",
       "      } else {\n",
       "        attempts++;\n",
       "        if (attempts > 100) {\n",
       "          clearInterval(timer);\n",
       "          console.log(\"Bokeh: ERROR: Unable to run BokehJS code because BokehJS library is missing\");\n",
       "        }\n",
       "      }\n",
       "    }, 10, root)\n",
       "  }\n",
       "})(window);"
      ],
      "application/vnd.bokehjs_exec.v0+json": ""
     },
     "metadata": {
      "application/vnd.bokehjs_exec.v0+json": {
       "id": "1195"
      }
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "show(plot)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Exercises"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Custom User Models\n",
    "\n",
    "It is possible to extend the set of built-in Bokeh models with your own custom user models. The capability opens some valuable use-cases:\n",
    "* customizing existing Bokeh model behaviour\n",
    "* wrapping and connecting other JS libraries to Bokeh\n",
    "\n",
    "With this capability, advanced users can try out new features or techniques easily, without having to set up a full Bokeh development environment. \n",
    "\n",
    "The section gives a basi outline of a custom model starts with a JavaScript implementation, which subclasses an existing BokehJS model. Full details can be found in the [Extending Bokeh](https://bokeh.pydata.org/en/latest/docs/user_guide/extensions.html) chapter of the User's Guide."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Implement the JavaScript Model"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [],
   "source": [
    "CODE =\"\"\"\n",
    "import {HTMLBox, HTMLBoxView} from \"models/layouts/html_box\"\n",
    "import {Slider} from \"models/widgets/slider\"\n",
    "import {div} from \"core/dom\"\n",
    "import * as p from \"core/properties\"\n",
    "export class CustomView extends HTMLBoxView {\n",
    "  model: Custom\n",
    "  private content_el: HTMLElement\n",
    "  connect_signals(): void {\n",
    "    super.connect_signals()\n",
    "    this.connect(this.model.slider.change, () => this._update_text())\n",
    "  }\n",
    "  render(): void {\n",
    "    // BokehJS Views create <div> elements by default, accessible as ``this.el``.\n",
    "    // Many Bokeh views extend this default <div> with additional elements\n",
    "    // (e.g. <canvas>), and instead do things like paint on the HTML canvas.\n",
    "    // In this case though, we change the contents of the <div>, based on the\n",
    "    // current slider value.\n",
    "    super.render()\n",
    "    this.content_el = div({style: {\n",
    "      textAlign: \"center\",\n",
    "      fontSize: \"1.2em\",\n",
    "      padding: \"2px\",\n",
    "      color: \"#b88d8e\",\n",
    "      backgroundColor: \"#2a3153\",\n",
    "    }})\n",
    "    this.el.appendChild(this.content_el)\n",
    "    this._update_text()\n",
    "  }\n",
    "  private _update_text(): void {\n",
    "    this.content_el.textContent = `${this.model.text}: ${this.model.slider.value}`\n",
    "  }\n",
    "}\n",
    "export namespace Custom {\n",
    "  export type Attrs = p.AttrsOf<Props>\n",
    "  export type Props = HTMLBox.Props & {\n",
    "    text: p.Property<string>\n",
    "    slider: p.Property<Slider>\n",
    "  }\n",
    "}\n",
    "export interface Custom extends Custom.Attrs {}\n",
    "export class Custom extends HTMLBox {\n",
    "  properties: Custom.Props\n",
    "  constructor(attrs?: Partial<Custom.Attrs>) {\n",
    "    super(attrs)\n",
    "  }\n",
    "  static initClass(): void {\n",
    "    // The ``type`` class attribute should generally match exactly the name\n",
    "    // of the corresponding Python class.\n",
    "    this.prototype.type = \"Custom\"\n",
    "    // If there is an associated view, this is typically boilerplate.\n",
    "    this.prototype.default_view = CustomView\n",
    "    // The define block adds corresponding \"properties\" to the JS model. These\n",
    "    // should normally line up 1-1 with the Python model class. Most property\n",
    "    // types have counterparts, e.g. bokeh.core.properties.String will be\n",
    "    // ``p.String`` in the JS implementation. Any time the JS type system is not\n",
    "    // yet as complete, you can use ``p.Any`` as a \"wildcard\" property type.\n",
    "    this.define<Custom.Props>({\n",
    "      text:   [ p.String ],\n",
    "      slider: [ p.Any    ],\n",
    "    })\n",
    "    this.override({\n",
    "      margin: 5,\n",
    "    })\n",
    "  }\n",
    "}\n",
    "Custom.initClass()\n",
    "\"\"\""
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Define the Python Model\n",
    "\n",
    "This JavaScript implementation is then attached to a corresponding Python Bokeh model:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [],
   "source": [
    "from bokeh.core.properties import String, Instance\n",
    "from bokeh.util.compiler import TypeScript\n",
    "from bokeh.models import HTMLBox, Slider\n",
    "\n",
    "class Custom(HTMLBox):\n",
    "\n",
    "    __implementation__ = TypeScript(CODE)\n",
    "\n",
    "    text = String(default=\"Custom text\")\n",
    "\n",
    "    slider = Instance(Slider)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Use the Python Model\n",
    "\n",
    "Then the new model can be used seamlessly in the same way as any built-in Bokeh model:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "\n",
       "    <div class=\"bk-root\">\n",
       "        <a href=\"https://bokeh.org\" target=\"_blank\" class=\"bk-logo bk-logo-small bk-logo-notebook\"></a>\n",
       "        <span id=\"1314\">Loading BokehJS ...</span>\n",
       "    </div>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "application/javascript": [
       "\n",
       "(function(root) {\n",
       "  function now() {\n",
       "    return new Date();\n",
       "  }\n",
       "\n",
       "  var force = true;\n",
       "\n",
       "  if (typeof root._bokeh_onload_callbacks === \"undefined\" || force === true) {\n",
       "    root._bokeh_onload_callbacks = [];\n",
       "    root._bokeh_is_loading = undefined;\n",
       "  }\n",
       "\n",
       "  var JS_MIME_TYPE = 'application/javascript';\n",
       "  var HTML_MIME_TYPE = 'text/html';\n",
       "  var EXEC_MIME_TYPE = 'application/vnd.bokehjs_exec.v0+json';\n",
       "  var CLASS_NAME = 'output_bokeh rendered_html';\n",
       "\n",
       "  /**\n",
       "   * Render data to the DOM node\n",
       "   */\n",
       "  function render(props, node) {\n",
       "    var script = document.createElement(\"script\");\n",
       "    node.appendChild(script);\n",
       "  }\n",
       "\n",
       "  /**\n",
       "   * Handle when an output is cleared or removed\n",
       "   */\n",
       "  function handleClearOutput(event, handle) {\n",
       "    var cell = handle.cell;\n",
       "\n",
       "    var id = cell.output_area._bokeh_element_id;\n",
       "    var server_id = cell.output_area._bokeh_server_id;\n",
       "    // Clean up Bokeh references\n",
       "    if (id != null && id in Bokeh.index) {\n",
       "      Bokeh.index[id].model.document.clear();\n",
       "      delete Bokeh.index[id];\n",
       "    }\n",
       "\n",
       "    if (server_id !== undefined) {\n",
       "      // Clean up Bokeh references\n",
       "      var cmd = \"from bokeh.io.state import curstate; print(curstate().uuid_to_server['\" + server_id + \"'].get_sessions()[0].document.roots[0]._id)\";\n",
       "      cell.notebook.kernel.execute(cmd, {\n",
       "        iopub: {\n",
       "          output: function(msg) {\n",
       "            var id = msg.content.text.trim();\n",
       "            if (id in Bokeh.index) {\n",
       "              Bokeh.index[id].model.document.clear();\n",
       "              delete Bokeh.index[id];\n",
       "            }\n",
       "          }\n",
       "        }\n",
       "      });\n",
       "      // Destroy server and session\n",
       "      var cmd = \"import bokeh.io.notebook as ion; ion.destroy_server('\" + server_id + \"')\";\n",
       "      cell.notebook.kernel.execute(cmd);\n",
       "    }\n",
       "  }\n",
       "\n",
       "  /**\n",
       "   * Handle when a new output is added\n",
       "   */\n",
       "  function handleAddOutput(event, handle) {\n",
       "    var output_area = handle.output_area;\n",
       "    var output = handle.output;\n",
       "\n",
       "    // limit handleAddOutput to display_data with EXEC_MIME_TYPE content only\n",
       "    if ((output.output_type != \"display_data\") || (!output.data.hasOwnProperty(EXEC_MIME_TYPE))) {\n",
       "      return\n",
       "    }\n",
       "\n",
       "    var toinsert = output_area.element.find(\".\" + CLASS_NAME.split(' ')[0]);\n",
       "\n",
       "    if (output.metadata[EXEC_MIME_TYPE][\"id\"] !== undefined) {\n",
       "      toinsert[toinsert.length - 1].firstChild.textContent = output.data[JS_MIME_TYPE];\n",
       "      // store reference to embed id on output_area\n",
       "      output_area._bokeh_element_id = output.metadata[EXEC_MIME_TYPE][\"id\"];\n",
       "    }\n",
       "    if (output.metadata[EXEC_MIME_TYPE][\"server_id\"] !== undefined) {\n",
       "      var bk_div = document.createElement(\"div\");\n",
       "      bk_div.innerHTML = output.data[HTML_MIME_TYPE];\n",
       "      var script_attrs = bk_div.children[0].attributes;\n",
       "      for (var i = 0; i < script_attrs.length; i++) {\n",
       "        toinsert[toinsert.length - 1].firstChild.setAttribute(script_attrs[i].name, script_attrs[i].value);\n",
       "        toinsert[toinsert.length - 1].firstChild.textContent = bk_div.children[0].textContent\n",
       "      }\n",
       "      // store reference to server id on output_area\n",
       "      output_area._bokeh_server_id = output.metadata[EXEC_MIME_TYPE][\"server_id\"];\n",
       "    }\n",
       "  }\n",
       "\n",
       "  function register_renderer(events, OutputArea) {\n",
       "\n",
       "    function append_mime(data, metadata, element) {\n",
       "      // create a DOM node to render to\n",
       "      var toinsert = this.create_output_subarea(\n",
       "        metadata,\n",
       "        CLASS_NAME,\n",
       "        EXEC_MIME_TYPE\n",
       "      );\n",
       "      this.keyboard_manager.register_events(toinsert);\n",
       "      // Render to node\n",
       "      var props = {data: data, metadata: metadata[EXEC_MIME_TYPE]};\n",
       "      render(props, toinsert[toinsert.length - 1]);\n",
       "      element.append(toinsert);\n",
       "      return toinsert\n",
       "    }\n",
       "\n",
       "    /* Handle when an output is cleared or removed */\n",
       "    events.on('clear_output.CodeCell', handleClearOutput);\n",
       "    events.on('delete.Cell', handleClearOutput);\n",
       "\n",
       "    /* Handle when a new output is added */\n",
       "    events.on('output_added.OutputArea', handleAddOutput);\n",
       "\n",
       "    /**\n",
       "     * Register the mime type and append_mime function with output_area\n",
       "     */\n",
       "    OutputArea.prototype.register_mime_type(EXEC_MIME_TYPE, append_mime, {\n",
       "      /* Is output safe? */\n",
       "      safe: true,\n",
       "      /* Index of renderer in `output_area.display_order` */\n",
       "      index: 0\n",
       "    });\n",
       "  }\n",
       "\n",
       "  // register the mime type if in Jupyter Notebook environment and previously unregistered\n",
       "  if (root.Jupyter !== undefined) {\n",
       "    var events = require('base/js/events');\n",
       "    var OutputArea = require('notebook/js/outputarea').OutputArea;\n",
       "\n",
       "    if (OutputArea.prototype.mime_types().indexOf(EXEC_MIME_TYPE) == -1) {\n",
       "      register_renderer(events, OutputArea);\n",
       "    }\n",
       "  }\n",
       "\n",
       "  \n",
       "  if (typeof (root._bokeh_timeout) === \"undefined\" || force === true) {\n",
       "    root._bokeh_timeout = Date.now() + 5000;\n",
       "    root._bokeh_failed_load = false;\n",
       "  }\n",
       "\n",
       "  var NB_LOAD_WARNING = {'data': {'text/html':\n",
       "     \"<div style='background-color: #fdd'>\\n\"+\n",
       "     \"<p>\\n\"+\n",
       "     \"BokehJS does not appear to have successfully loaded. If loading BokehJS from CDN, this \\n\"+\n",
       "     \"may be due to a slow or bad network connection. Possible fixes:\\n\"+\n",
       "     \"</p>\\n\"+\n",
       "     \"<ul>\\n\"+\n",
       "     \"<li>re-rerun `output_notebook()` to attempt to load from CDN again, or</li>\\n\"+\n",
       "     \"<li>use INLINE resources instead, as so:</li>\\n\"+\n",
       "     \"</ul>\\n\"+\n",
       "     \"<code>\\n\"+\n",
       "     \"from bokeh.resources import INLINE\\n\"+\n",
       "     \"output_notebook(resources=INLINE)\\n\"+\n",
       "     \"</code>\\n\"+\n",
       "     \"</div>\"}};\n",
       "\n",
       "  function display_loaded() {\n",
       "    var el = document.getElementById(\"1314\");\n",
       "    if (el != null) {\n",
       "      el.textContent = \"BokehJS is loading...\";\n",
       "    }\n",
       "    if (root.Bokeh !== undefined) {\n",
       "      if (el != null) {\n",
       "        el.textContent = \"BokehJS \" + root.Bokeh.version + \" successfully loaded.\";\n",
       "      }\n",
       "    } else if (Date.now() < root._bokeh_timeout) {\n",
       "      setTimeout(display_loaded, 100)\n",
       "    }\n",
       "  }\n",
       "\n",
       "\n",
       "  function run_callbacks() {\n",
       "    try {\n",
       "      root._bokeh_onload_callbacks.forEach(function(callback) {\n",
       "        if (callback != null)\n",
       "          callback();\n",
       "      });\n",
       "    } finally {\n",
       "      delete root._bokeh_onload_callbacks\n",
       "    }\n",
       "    console.debug(\"Bokeh: all callbacks have finished\");\n",
       "  }\n",
       "\n",
       "  function load_libs(css_urls, js_urls, callback) {\n",
       "    if (css_urls == null) css_urls = [];\n",
       "    if (js_urls == null) js_urls = [];\n",
       "\n",
       "    root._bokeh_onload_callbacks.push(callback);\n",
       "    if (root._bokeh_is_loading > 0) {\n",
       "      console.debug(\"Bokeh: BokehJS is being loaded, scheduling callback at\", now());\n",
       "      return null;\n",
       "    }\n",
       "    if (js_urls == null || js_urls.length === 0) {\n",
       "      run_callbacks();\n",
       "      return null;\n",
       "    }\n",
       "    console.debug(\"Bokeh: BokehJS not loaded, scheduling load and callback at\", now());\n",
       "    root._bokeh_is_loading = css_urls.length + js_urls.length;\n",
       "\n",
       "    function on_load() {\n",
       "      root._bokeh_is_loading--;\n",
       "      if (root._bokeh_is_loading === 0) {\n",
       "        console.debug(\"Bokeh: all BokehJS libraries/stylesheets loaded\");\n",
       "        run_callbacks()\n",
       "      }\n",
       "    }\n",
       "\n",
       "    function on_error() {\n",
       "      console.error(\"failed to load \" + url);\n",
       "    }\n",
       "\n",
       "    for (var i = 0; i < css_urls.length; i++) {\n",
       "      var url = css_urls[i];\n",
       "      const element = document.createElement(\"link\");\n",
       "      element.onload = on_load;\n",
       "      element.onerror = on_error;\n",
       "      element.rel = \"stylesheet\";\n",
       "      element.type = \"text/css\";\n",
       "      element.href = url;\n",
       "      console.debug(\"Bokeh: injecting link tag for BokehJS stylesheet: \", url);\n",
       "      document.body.appendChild(element);\n",
       "    }\n",
       "\n",
       "    const hashes = {\"https://cdn.bokeh.org/bokeh/release/bokeh-2.0.1.min.js\": \"JpP8FXbgAZLkfur7LiK3j9AGBhHNIvF742meBJrjO2ShJDhCG2I1uVvW+0DUtrmc\", \"https://cdn.bokeh.org/bokeh/release/bokeh-widgets-2.0.1.min.js\": \"xZlADit0Q04ISQEdKg2k3L4W9AwQBAuDs9nJL9fM/WwzL1tEU9VPNezOFX0nLEAz\", \"https://cdn.bokeh.org/bokeh/release/bokeh-tables-2.0.1.min.js\": \"4BuPRZkdMKSnj3zoxiNrQ86XgNw0rYmBOxe7nshquXwwcauupgBF2DHLVG1WuZlV\", \"https://cdn.bokeh.org/bokeh/release/bokeh-gl-2.0.1.min.js\": \"Dv1SQ87hmDqK6S5OhBf0bCuwAEvL5QYL0PuR/F1SPVhCS/r/abjkbpKDYL2zeM19\"};\n",
       "\n",
       "    for (var i = 0; i < js_urls.length; i++) {\n",
       "      var url = js_urls[i];\n",
       "      var element = document.createElement('script');\n",
       "      element.onload = on_load;\n",
       "      element.onerror = on_error;\n",
       "      element.async = false;\n",
       "      element.src = url;\n",
       "      if (url in hashes) {\n",
       "        element.crossOrigin = \"anonymous\";\n",
       "        element.integrity = \"sha384-\" + hashes[url];\n",
       "      }\n",
       "      console.debug(\"Bokeh: injecting script tag for BokehJS library: \", url);\n",
       "      document.head.appendChild(element);\n",
       "    }\n",
       "  };var element = document.getElementById(\"1314\");\n",
       "  if (element == null) {\n",
       "    console.error(\"Bokeh: ERROR: autoload.js configured with elementid '1314' but no matching script tag was found. \")\n",
       "    return false;\n",
       "  }\n",
       "\n",
       "  function inject_raw_css(css) {\n",
       "    const element = document.createElement(\"style\");\n",
       "    element.appendChild(document.createTextNode(css));\n",
       "    document.body.appendChild(element);\n",
       "  }\n",
       "\n",
       "  \n",
       "  var js_urls = [\"https://cdn.bokeh.org/bokeh/release/bokeh-2.0.1.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-widgets-2.0.1.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-tables-2.0.1.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-gl-2.0.1.min.js\"];\n",
       "  var css_urls = [];\n",
       "  \n",
       "\n",
       "  var inline_js = [\n",
       "    function(Bokeh) {\n",
       "      Bokeh.set_log_level(\"info\");\n",
       "    },\n",
       "    \n",
       "    function(Bokeh) {\n",
       "      (function(root, factory) {\n",
       "          factory(root[\"Bokeh\"]);\n",
       "      })(this, function(Bokeh) {\n",
       "        var define;\n",
       "        return (function outer(modules, entry) {\n",
       "        if (Bokeh != null) {\n",
       "          return Bokeh.register_plugin(modules, entry);\n",
       "        } else {\n",
       "          throw new Error(\"Cannot find Bokeh. You have to load it prior to loading plugins.\");\n",
       "        }\n",
       "      })\n",
       "      ({\n",
       "        \"custom/main\": function(require, module, exports) {\n",
       "          var models = {\n",
       "            \"Custom\": require(\"custom/custom\").Custom\n",
       "          };\n",
       "          require(\"base\").register_models(models);\n",
       "          module.exports = models;\n",
       "        },\n",
       "        \"custom/custom\": function(require, module, exports) {\n",
       "      \"use strict\";\n",
       "      var __importStar = (this && this.__importStar) || function (mod) {\n",
       "          if (mod && mod.__esModule) return mod;\n",
       "          var result = {};\n",
       "          if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];\n",
       "          result[\"default\"] = mod;\n",
       "          return result;\n",
       "      };\n",
       "      Object.defineProperty(exports, \"__esModule\", { value: true });\n",
       "      const html_box_1 = require(\"models/layouts/html_box\");\n",
       "      const dom_1 = require(\"core/dom\");\n",
       "      const p = __importStar(require(\"core/properties\"));\n",
       "      class CustomView extends html_box_1.HTMLBoxView {\n",
       "          connect_signals() {\n",
       "              super.connect_signals();\n",
       "              this.connect(this.model.slider.change, () => this._update_text());\n",
       "          }\n",
       "          render() {\n",
       "              // BokehJS Views create <div> elements by default, accessible as ``this.el``.\n",
       "              // Many Bokeh views extend this default <div> with additional elements\n",
       "              // (e.g. <canvas>), and instead do things like paint on the HTML canvas.\n",
       "              // In this case though, we change the contents of the <div>, based on the\n",
       "              // current slider value.\n",
       "              super.render();\n",
       "              this.content_el = dom_1.div({ style: {\n",
       "                      textAlign: \"center\",\n",
       "                      fontSize: \"1.2em\",\n",
       "                      padding: \"2px\",\n",
       "                      color: \"#b88d8e\",\n",
       "                      backgroundColor: \"#2a3153\",\n",
       "                  } });\n",
       "              this.el.appendChild(this.content_el);\n",
       "              this._update_text();\n",
       "          }\n",
       "          _update_text() {\n",
       "              this.content_el.textContent = `${this.model.text}: ${this.model.slider.value}`;\n",
       "          }\n",
       "      }\n",
       "      exports.CustomView = CustomView;\n",
       "      CustomView.__name__ = \"CustomView\";\n",
       "      class Custom extends html_box_1.HTMLBox {\n",
       "          constructor(attrs) {\n",
       "              super(attrs);\n",
       "          }\n",
       "          static initClass() {\n",
       "              // The ``type`` class attribute should generally match exactly the name\n",
       "              // of the corresponding Python class.\n",
       "              this.prototype.type = \"Custom\";\n",
       "              // If there is an associated view, this is typically boilerplate.\n",
       "              this.prototype.default_view = CustomView;\n",
       "              // The define block adds corresponding \"properties\" to the JS model. These\n",
       "              // should normally line up 1-1 with the Python model class. Most property\n",
       "              // types have counterparts, e.g. bokeh.core.properties.String will be\n",
       "              // ``p.String`` in the JS implementation. Any time the JS type system is not\n",
       "              // yet as complete, you can use ``p.Any`` as a \"wildcard\" property type.\n",
       "              this.define({\n",
       "                  text: [p.String],\n",
       "                  slider: [p.Any],\n",
       "              });\n",
       "              this.override({\n",
       "                  margin: 5,\n",
       "              });\n",
       "          }\n",
       "      }\n",
       "      exports.Custom = Custom;\n",
       "      Custom.__name__ = \"Custom\";\n",
       "      Custom.initClass();\n",
       "      //# sourceMappingURL=Custom.js.map\n",
       "      }\n",
       "      }, \"custom/main\");\n",
       "      ;\n",
       "      });\n",
       "\n",
       "    },\n",
       "    function(Bokeh) {\n",
       "    \n",
       "    \n",
       "    }\n",
       "  ];\n",
       "\n",
       "  function run_inline_js() {\n",
       "    \n",
       "    if (root.Bokeh !== undefined || force === true) {\n",
       "      \n",
       "    for (var i = 0; i < inline_js.length; i++) {\n",
       "      inline_js[i].call(root, root.Bokeh);\n",
       "    }\n",
       "    if (force === true) {\n",
       "        display_loaded();\n",
       "      }} else if (Date.now() < root._bokeh_timeout) {\n",
       "      setTimeout(run_inline_js, 100);\n",
       "    } else if (!root._bokeh_failed_load) {\n",
       "      console.log(\"Bokeh: BokehJS failed to load within specified timeout.\");\n",
       "      root._bokeh_failed_load = true;\n",
       "    } else if (force !== true) {\n",
       "      var cell = $(document.getElementById(\"1314\")).parents('.cell').data().cell;\n",
       "      cell.output_area.append_execute_result(NB_LOAD_WARNING)\n",
       "    }\n",
       "\n",
       "  }\n",
       "\n",
       "  if (root._bokeh_is_loading === 0) {\n",
       "    console.debug(\"Bokeh: BokehJS loaded, going straight to plotting\");\n",
       "    run_inline_js();\n",
       "  } else {\n",
       "    load_libs(css_urls, js_urls, function() {\n",
       "      console.debug(\"Bokeh: BokehJS plotting callback run at\", now());\n",
       "      run_inline_js();\n",
       "    });\n",
       "  }\n",
       "}(window));"
      ],
      "application/vnd.bokehjs_load.v0+json": "\n(function(root) {\n  function now() {\n    return new Date();\n  }\n\n  var force = true;\n\n  if (typeof root._bokeh_onload_callbacks === \"undefined\" || force === true) {\n    root._bokeh_onload_callbacks = [];\n    root._bokeh_is_loading = undefined;\n  }\n\n  \n\n  \n  if (typeof (root._bokeh_timeout) === \"undefined\" || force === true) {\n    root._bokeh_timeout = Date.now() + 5000;\n    root._bokeh_failed_load = false;\n  }\n\n  var NB_LOAD_WARNING = {'data': {'text/html':\n     \"<div style='background-color: #fdd'>\\n\"+\n     \"<p>\\n\"+\n     \"BokehJS does not appear to have successfully loaded. If loading BokehJS from CDN, this \\n\"+\n     \"may be due to a slow or bad network connection. Possible fixes:\\n\"+\n     \"</p>\\n\"+\n     \"<ul>\\n\"+\n     \"<li>re-rerun `output_notebook()` to attempt to load from CDN again, or</li>\\n\"+\n     \"<li>use INLINE resources instead, as so:</li>\\n\"+\n     \"</ul>\\n\"+\n     \"<code>\\n\"+\n     \"from bokeh.resources import INLINE\\n\"+\n     \"output_notebook(resources=INLINE)\\n\"+\n     \"</code>\\n\"+\n     \"</div>\"}};\n\n  function display_loaded() {\n    var el = document.getElementById(\"1314\");\n    if (el != null) {\n      el.textContent = \"BokehJS is loading...\";\n    }\n    if (root.Bokeh !== undefined) {\n      if (el != null) {\n        el.textContent = \"BokehJS \" + root.Bokeh.version + \" successfully loaded.\";\n      }\n    } else if (Date.now() < root._bokeh_timeout) {\n      setTimeout(display_loaded, 100)\n    }\n  }\n\n\n  function run_callbacks() {\n    try {\n      root._bokeh_onload_callbacks.forEach(function(callback) {\n        if (callback != null)\n          callback();\n      });\n    } finally {\n      delete root._bokeh_onload_callbacks\n    }\n    console.debug(\"Bokeh: all callbacks have finished\");\n  }\n\n  function load_libs(css_urls, js_urls, callback) {\n    if (css_urls == null) css_urls = [];\n    if (js_urls == null) js_urls = [];\n\n    root._bokeh_onload_callbacks.push(callback);\n    if (root._bokeh_is_loading > 0) {\n      console.debug(\"Bokeh: BokehJS is being loaded, scheduling callback at\", now());\n      return null;\n    }\n    if (js_urls == null || js_urls.length === 0) {\n      run_callbacks();\n      return null;\n    }\n    console.debug(\"Bokeh: BokehJS not loaded, scheduling load and callback at\", now());\n    root._bokeh_is_loading = css_urls.length + js_urls.length;\n\n    function on_load() {\n      root._bokeh_is_loading--;\n      if (root._bokeh_is_loading === 0) {\n        console.debug(\"Bokeh: all BokehJS libraries/stylesheets loaded\");\n        run_callbacks()\n      }\n    }\n\n    function on_error() {\n      console.error(\"failed to load \" + url);\n    }\n\n    for (var i = 0; i < css_urls.length; i++) {\n      var url = css_urls[i];\n      const element = document.createElement(\"link\");\n      element.onload = on_load;\n      element.onerror = on_error;\n      element.rel = \"stylesheet\";\n      element.type = \"text/css\";\n      element.href = url;\n      console.debug(\"Bokeh: injecting link tag for BokehJS stylesheet: \", url);\n      document.body.appendChild(element);\n    }\n\n    const hashes = {\"https://cdn.bokeh.org/bokeh/release/bokeh-2.0.1.min.js\": \"JpP8FXbgAZLkfur7LiK3j9AGBhHNIvF742meBJrjO2ShJDhCG2I1uVvW+0DUtrmc\", \"https://cdn.bokeh.org/bokeh/release/bokeh-widgets-2.0.1.min.js\": \"xZlADit0Q04ISQEdKg2k3L4W9AwQBAuDs9nJL9fM/WwzL1tEU9VPNezOFX0nLEAz\", \"https://cdn.bokeh.org/bokeh/release/bokeh-tables-2.0.1.min.js\": \"4BuPRZkdMKSnj3zoxiNrQ86XgNw0rYmBOxe7nshquXwwcauupgBF2DHLVG1WuZlV\", \"https://cdn.bokeh.org/bokeh/release/bokeh-gl-2.0.1.min.js\": \"Dv1SQ87hmDqK6S5OhBf0bCuwAEvL5QYL0PuR/F1SPVhCS/r/abjkbpKDYL2zeM19\"};\n\n    for (var i = 0; i < js_urls.length; i++) {\n      var url = js_urls[i];\n      var element = document.createElement('script');\n      element.onload = on_load;\n      element.onerror = on_error;\n      element.async = false;\n      element.src = url;\n      if (url in hashes) {\n        element.crossOrigin = \"anonymous\";\n        element.integrity = \"sha384-\" + hashes[url];\n      }\n      console.debug(\"Bokeh: injecting script tag for BokehJS library: \", url);\n      document.head.appendChild(element);\n    }\n  };var element = document.getElementById(\"1314\");\n  if (element == null) {\n    console.error(\"Bokeh: ERROR: autoload.js configured with elementid '1314' but no matching script tag was found. \")\n    return false;\n  }\n\n  function inject_raw_css(css) {\n    const element = document.createElement(\"style\");\n    element.appendChild(document.createTextNode(css));\n    document.body.appendChild(element);\n  }\n\n  \n  var js_urls = [\"https://cdn.bokeh.org/bokeh/release/bokeh-2.0.1.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-widgets-2.0.1.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-tables-2.0.1.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-gl-2.0.1.min.js\"];\n  var css_urls = [];\n  \n\n  var inline_js = [\n    function(Bokeh) {\n      Bokeh.set_log_level(\"info\");\n    },\n    \n    function(Bokeh) {\n      (function(root, factory) {\n          factory(root[\"Bokeh\"]);\n      })(this, function(Bokeh) {\n        var define;\n        return (function outer(modules, entry) {\n        if (Bokeh != null) {\n          return Bokeh.register_plugin(modules, entry);\n        } else {\n          throw new Error(\"Cannot find Bokeh. You have to load it prior to loading plugins.\");\n        }\n      })\n      ({\n        \"custom/main\": function(require, module, exports) {\n          var models = {\n            \"Custom\": require(\"custom/custom\").Custom\n          };\n          require(\"base\").register_models(models);\n          module.exports = models;\n        },\n        \"custom/custom\": function(require, module, exports) {\n      \"use strict\";\n      var __importStar = (this && this.__importStar) || function (mod) {\n          if (mod && mod.__esModule) return mod;\n          var result = {};\n          if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];\n          result[\"default\"] = mod;\n          return result;\n      };\n      Object.defineProperty(exports, \"__esModule\", { value: true });\n      const html_box_1 = require(\"models/layouts/html_box\");\n      const dom_1 = require(\"core/dom\");\n      const p = __importStar(require(\"core/properties\"));\n      class CustomView extends html_box_1.HTMLBoxView {\n          connect_signals() {\n              super.connect_signals();\n              this.connect(this.model.slider.change, () => this._update_text());\n          }\n          render() {\n              // BokehJS Views create <div> elements by default, accessible as ``this.el``.\n              // Many Bokeh views extend this default <div> with additional elements\n              // (e.g. <canvas>), and instead do things like paint on the HTML canvas.\n              // In this case though, we change the contents of the <div>, based on the\n              // current slider value.\n              super.render();\n              this.content_el = dom_1.div({ style: {\n                      textAlign: \"center\",\n                      fontSize: \"1.2em\",\n                      padding: \"2px\",\n                      color: \"#b88d8e\",\n                      backgroundColor: \"#2a3153\",\n                  } });\n              this.el.appendChild(this.content_el);\n              this._update_text();\n          }\n          _update_text() {\n              this.content_el.textContent = `${this.model.text}: ${this.model.slider.value}`;\n          }\n      }\n      exports.CustomView = CustomView;\n      CustomView.__name__ = \"CustomView\";\n      class Custom extends html_box_1.HTMLBox {\n          constructor(attrs) {\n              super(attrs);\n          }\n          static initClass() {\n              // The ``type`` class attribute should generally match exactly the name\n              // of the corresponding Python class.\n              this.prototype.type = \"Custom\";\n              // If there is an associated view, this is typically boilerplate.\n              this.prototype.default_view = CustomView;\n              // The define block adds corresponding \"properties\" to the JS model. These\n              // should normally line up 1-1 with the Python model class. Most property\n              // types have counterparts, e.g. bokeh.core.properties.String will be\n              // ``p.String`` in the JS implementation. Any time the JS type system is not\n              // yet as complete, you can use ``p.Any`` as a \"wildcard\" property type.\n              this.define({\n                  text: [p.String],\n                  slider: [p.Any],\n              });\n              this.override({\n                  margin: 5,\n              });\n          }\n      }\n      exports.Custom = Custom;\n      Custom.__name__ = \"Custom\";\n      Custom.initClass();\n      //# sourceMappingURL=Custom.js.map\n      }\n      }, \"custom/main\");\n      ;\n      });\n\n    },\n    function(Bokeh) {\n    \n    \n    }\n  ];\n\n  function run_inline_js() {\n    \n    if (root.Bokeh !== undefined || force === true) {\n      \n    for (var i = 0; i < inline_js.length; i++) {\n      inline_js[i].call(root, root.Bokeh);\n    }\n    if (force === true) {\n        display_loaded();\n      }} else if (Date.now() < root._bokeh_timeout) {\n      setTimeout(run_inline_js, 100);\n    } else if (!root._bokeh_failed_load) {\n      console.log(\"Bokeh: BokehJS failed to load within specified timeout.\");\n      root._bokeh_failed_load = true;\n    } else if (force !== true) {\n      var cell = $(document.getElementById(\"1314\")).parents('.cell').data().cell;\n      cell.output_area.append_execute_result(NB_LOAD_WARNING)\n    }\n\n  }\n\n  if (root._bokeh_is_loading === 0) {\n    console.debug(\"Bokeh: BokehJS loaded, going straight to plotting\");\n    run_inline_js();\n  } else {\n    load_libs(css_urls, js_urls, function() {\n      console.debug(\"Bokeh: BokehJS plotting callback run at\", now());\n      run_inline_js();\n    });\n  }\n}(window));"
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "from bokeh.io import show, output_file\n",
    "from bokeh.layouts import column\n",
    "from bokeh.models import Slider\n",
    "\n",
    "slider = Slider(start=0, end=10, step=0.1, value=0, title=\"value\")\n",
    "\n",
    "custom = Custom(text=\"Special Slider Display\", slider=slider)\n",
    "\n",
    "layout = column(slider, custom)\n",
    "\n",
    "# Necessary to explicitly reload BokehJS to pick up new extension code\n",
    "output_notebook()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "\n",
       "\n",
       "\n",
       "\n",
       "\n",
       "\n",
       "  <div class=\"bk-root\" id=\"5da39cdb-0123-4f4f-9811-a3237fce00bf\" data-root-id=\"1313\"></div>\n"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "application/javascript": [
       "(function(root) {\n",
       "  function embed_document(root) {\n",
       "    \n",
       "  var docs_json = {\"6413a635-10e2-46be-869b-4ffd006252c1\":{\"roots\":{\"references\":[{\"attributes\":{\"children\":[{\"id\":\"1311\"},{\"id\":\"1312\"}]},\"id\":\"1313\",\"type\":\"Column\"},{\"attributes\":{\"slider\":{\"id\":\"1311\"},\"text\":\"Special Slider Display\"},\"id\":\"1312\",\"type\":\"Custom\"},{\"attributes\":{\"end\":10,\"format\":\"0[.]00\",\"start\":0,\"step\":0.1,\"title\":\"value\",\"value\":0},\"id\":\"1311\",\"type\":\"Slider\"}],\"root_ids\":[\"1313\"]},\"title\":\"Bokeh Application\",\"version\":\"2.0.1\"}};\n",
       "  var render_items = [{\"docid\":\"6413a635-10e2-46be-869b-4ffd006252c1\",\"root_ids\":[\"1313\"],\"roots\":{\"1313\":\"5da39cdb-0123-4f4f-9811-a3237fce00bf\"}}];\n",
       "  root.Bokeh.embed.embed_items_notebook(docs_json, render_items);\n",
       "\n",
       "  }\n",
       "  if (root.Bokeh !== undefined) {\n",
       "    embed_document(root);\n",
       "  } else {\n",
       "    var attempts = 0;\n",
       "    var timer = setInterval(function(root) {\n",
       "      if (root.Bokeh !== undefined) {\n",
       "        clearInterval(timer);\n",
       "        embed_document(root);\n",
       "      } else {\n",
       "        attempts++;\n",
       "        if (attempts > 100) {\n",
       "          clearInterval(timer);\n",
       "          console.log(\"Bokeh: ERROR: Unable to run BokehJS code because BokehJS library is missing\");\n",
       "        }\n",
       "      }\n",
       "    }, 10, root)\n",
       "  }\n",
       "})(window);"
      ],
      "application/vnd.bokehjs_exec.v0+json": ""
     },
     "metadata": {
      "application/vnd.bokehjs_exec.v0+json": {
       "id": "1313"
      }
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "show(layout)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Next Section"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "To explore the next topic in the appendices, click on this link: [A2 - Visualizing Big Data with Datashader](A2%20-%20Visualizing%20Big%20Data%20with%20Datashader.ipynb)\n",
    "\n",
    "To go back to the overview, click [here](00%20-%20Introduction%20and%20Setup.ipynb)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "anaconda-cloud": {},
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.2"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
